Chrome plugin running a function but not found - google-chrome-extension

I want my plugin to inject so that when i click on the button, it runs a function in the current tab. However, it's giving me a function not found error.. is there some way to do this?
This is my popup.html:
<script>
function start() {
chrome.tabs.executeScript( null,
{ code: "func_in_body()",
allFrames: true }
);
}
start();
</script>
and even though the function is in the page, it gives me an error
The error is:
Uncaught ReferenceError: func_in_body is not defined
(anonymous function)
though one of the buttons' onclick uses it. I'm not sure if there's a scope issue or not.

JavaScript you inject into tabs is executed in a isolated environment and does not have access to the pages JavaScript. You can read more about it in the documentation.

You don't need a popup to execute a function or a script on the current tab. What I did is, I made a background page with:
<html>
<head>
<script>
var iconName = "icon.png";
chrome.browserAction.setIcon({path:iconName});
function onClicked(){
chrome.tabs.executeScript(null, {file: "content_script.js"});
}
chrome.browserAction.onClicked.addListener(onClicked);
</script>
</head>
</html>
the background page has to be defined in the manifest.json
...
"background_page": "background.html",
...
and the last thing just create the content_script.js (or whatever you called it) and enter your code there.
Edit:
Don't forget to add the permissions that your script can be executed on every site
...
"permissions": [
"tabs",
"http://*/*",
"https://*/*"
],
...

Related

Chrome extension: detect when popup window closes

For my chrome extension, I want to perform an action when the browserAction popup window closes. I understand that there no built-in events are triggered when this happens. I found this suggestion to open a connection with the background script, and then use the connection's port.onDisconnect event to detect that the popup window is closing.
However, when the popup window closes, I see the following error in the Developer Console for the background script:
(BLESSED_EXTENSION context for glkehflnlfekdijfhacccflbffbjhgbd) extensions::messaging:102: Uncaught TypeError: Cannot read property 'destroy_' of undefined{TypeError: Cannot read property 'destroy_' of undefined
at PortImpl.destroy_ (extensions::messaging:102:37)
at dispatchOnDisconnect (extensions::messaging:322:29)}
The scripts that I use are detailed below.
Can you see where I am going wrong?
manifest.json
{ "manifest_version": 2
, "name": "Detect when popup closes"
, "version": "0.1"
, "browser_action": {
"default_icon": "popup.png"
, "default_popup": "popup.html"
}
, "background": {
"scripts": [
"background.js"
]
}
}
popup.html
<!DOCTYPE html>
<body>
<h1>Test</h1>
<script src="popup.js"></script>
</body>
</html>
popup.js
var port = chrome.runtime.connect()
background.js
chrome.runtime.onConnect.addListener(function (externalPort) {
externalPort.onDisconnect = function () {
try {
var ignoreError = chrome.runtime.lastError
} catch (error) {
console.log("onDisconnect")
}
}
)
For reference, here's the working version of the background.js script:
chrome.runtime.onConnect.addListener(function (externalPort) {
externalPort.onDisconnect.addListener(function () {
console.log("onDisconnect")
// Do stuff that should happen when popup window closes here
})
console.log("onConnect")
})
onDisconnect is not an assignable property.
It's an object that provides addListener method to register a callback:
externalPort.onDisconnect.addListener(function() {
var ignoreError = chrome.runtime.lastError;
console.log("onDisconnect");
});

RequireJS does not run data-main script before loading required modules

My project includes the following files:
./index.html
./js/main.js
./js/vendor/require.js
./js/viewmodel/vm.js
The index.html has the following relevant snippet:
<script data-main="js/main.js" src="js/vendor/require.js"></script>
<script type="text/javascript">
require(['viewmodel/vm', 'ko'],
function(viewmodel, ko) {
ko.applyBindings(viewmodel);
}
);
</script>
The js/main.js file is as follows:
var root = this;
define('jquery', ['http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.3.js'], function () { return root.$; });
define('ko', ['http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.1.0.js'], function (ko) { return ko; });
The js/viewmodel/vm.js file...
define(['jquery', 'ko'],
function($, ko) {
return {
subject: 'world',
greeting: 'hello'
}
}
);
When you open a browser to index.html, then the browser tries to load a file called js/ko.js instead of using the module defined in main.js. It seems like the js file pointed to by the data-main attribute is not guaranteed to run before dependency resolution. This does not seem correct to me since one purpose of the data-main js file is to define require configuration (i.e. path, shim, etc). I am using require v2.1.2.
This works perfectly fine if I copy the contents of my main.js file into the script block in index.html. By "perfectly fine" I mean that it resolved ko to be a module and finds the appropriate CDN link to resolve ko instead of trying to download ./js/ko.js.
to use the data-main attribute for configuring your whole application, it is necessary that it is the single entry point for all your code.
your 2nd script block breaks this requirement by providing a 2nd entry point. since these entry points will resolve independently of each other (and asynchronously), you cannot rely on one to affect the other.
to resolve it, refactor your code in a way that provides a single entry point to your application and do your configuration via this entry point.
That's because requirejs sets the async. Attribute on the script.
The boolean async attribute on script elements allows the external
JavaScript file to run when it's available, without delaying page load
first.
This means that both scripts are loaded and evaluated parallel, so none of the two scripts can access methods or functions from the other one.
If you want to define requirejs variables in one script you mustn't load that script with require js.
For me there are three possibilities how you can solve that problem:
Add the content of main.js to your page (as you mention)
Load the main.js file without requirejs as normal script
Define the require config before loading the scripts (link to requirejs docu )
I had the same problem. The architecture of the site that i was working was components that was loading asynchronous at each part of the page.
Each component has its own html, css, and js code.
So, my solution is to keep a guard function for all the required dependency code, to protect them from running before the main javascript file:
index.html
<head>
<script type="text/javascript">
window.BeforeMainGuard = {
beforeMainLoadedFunctions: [],
hasMainLoaded: false,
guard: function( func ) {
console.assert( typeof func === 'function' );
if( this.hasMainLoaded ) {
func();
}else {
this.beforeMainLoadedFunctions.push( func );
}
},
onMainLoaded: function() {
for( var i = 0; i<this.beforeMainLoadedFunctions.length; ++i ) {
var beforeMainLoadedFunction = this.beforeMainLoadedFunctions[i];
beforeMainLoadedFunction();
}
this.beforeMainLoadedFunctions = null;
this.hasMainLoaded = true;
}
};
</script>
<script data-main="js/main.js" src="js/vendor/require.js"></script>
<script type="text/javascript">
window.BeforeMainGuard.guard( function() {
require(['viewmodel/vm', 'ko'],
function(viewmodel, ko) {
ko.applyBindings(viewmodel);
}
);
});
</script>
</head>
js/main.js
require.config({
// your config
});
require( [ 'AppLogic' ], function( AppLogic ){
AppLogic.Init();
window.BeforeMainGuard.onMainLoaded();
} );

requirejs , multipage , weird order of execution

I am using requirejs for a multipage application. I am having a weird issue with the order of execution of the main files. Here is my setup.
In all of my html pages ( This main has all the common js files that are required by all my pages)
then on each page (say page1.html):
<script data-main="main" src="require.js" />
<script>
require(['require','main'],function(require) {
require(['main-library']);
});
</script>
My main.js looks like:
require.config({
paths:{
'lib':'../lib',
'jquery': '../lib/jquery/jquery-1.7.1.min',
'jquery.iframe-transport' : '../lib/jquery.iframe-transport.min.js'
},
deps:['jquery','jquery-ui'],
shim: {
<other shim files>
},
});
require([<list of all other dependencies>], function() {
});
And the main-library.js looks like:
define('main-library',
['main',
'jquery.iframe-transport',
<other dependencies>
],
function() {
}
);
My expectation:
Was that the "main-library" will not start to load until the "main" and all the dependencies specified in the main are not loaded. I expect this since I require "main-library" in a nested require call on each page. see above.
The issue:
When my page1.html loads, it looks like the main-library.js starts loading even before the main.js has finished loading and main-library fails to get the require.config and hence the paths for the dependencies are not seen by it.
What am I doing wrong ?
Answering my own question. It was infact another require down in that page1.html that was loading the "main-library" again. So I had something like:
<script data-main="main" src="require.js" />
<script>
require(['require','main'],function(require) {
require(['main-library']);
});
</script>
...
... Other html ...
...
<script>
require(['main-library'],function(require) {
//do something.
});
</script>
This second require on main-library was the one that was failing to find the paths. Wrapping it with a require on main worked.

how to run nodejs flatiron/director example

I tried to run the https://github.com/flatiron/director#client-side example to get familiar with director.js.
I am not able to setup the flatiron module on the client-side.
In my html page (say, <my_project>/page.html) I replaced the location of director.js with
a location of its counterpart from my project:
<my_project>/node_modules/flatiron/node_modules/director/lib/director.js
Upon loading the <my_project>/page.html page in the browser
I got errors: export and Router not defined.
First idea: After all, on the browser side there is no nodejs...
Ok, I thought that browserify could help me with it.
I generated a single 'browser-side' bundle (was it necessary?):
my_project> node node_modules/browserify/bin/cli.js node_modules/flatiron/node_modules/director/lib director.js -o cs_director.js
and I used it in the line: <script src="cs_director.js"></script>
The problem is that the error
Uncaught ReferenceError: Router is not defined
(anonymous function)
still appears so I guess the whole example will not work.
I am new to node/js and I am not sure if it makes sens what I have done in my case described above...
Does anybody how to solve it?
Or generally, how to use 'isomorphic' stuff on a browser-side?
The html examples on Github just refer to the same .js files
as server-side examples ...
Can you recommend any tutorials, examples?
Thanks,
-gvlax
You can find a browser-specific build of director here which has all of the server code stripped away.
Thanks DeadDEnD,
Now it works like a charm!
I have no idea how I could missed that info in readme ... I read the manual first, I swear:-)
Here is my sample code:
<!html>
<html>
<head>
<script src="director-1.0.7.min.js"></script>
<script>
var author = function () { console.log("author called.");},
books = function () { console.log("books called."); },
viewBook = function(bookId) { console.log("viewBook called."); };
var routes = {
'/author': author,
'/books': [books, function() { console.log("anonymous fun called."); }],
'/books/view/:bookId': viewBook
};
var router = Router(routes);
router.init();
</script>
</head>
<body>
Click me to call two functions at a time.
</body>
</html>
--gvlax

Debugging injected content scripts

I have a lot of code that I only want to run when the user clicks the extension icon. I'd rather not have it run for every tab opened. Thus using the content_scripts entry in the manifest file isn't the best option. However, I haven't been able to see the content scripts show up in the list of scripts in the developer tools when I programatically inject scripts. I'm fine developing for now with content scripts, but at some point I'd like to avoid it.
I run logging all over the place, and perform message passing as well. So I know very well that these scripts are successfully getting injected and running, but they simply fail to show up in the file list.
In code, the following works just dandy (in the manifest):
{
// ...
"content_scripts": [{
"matches": ["<all_urls>"],
"css": ["style/content.css"],
"js": [
"closure/goog/base.js",
"closure/goog/deps.js",
"util.js",
"AddressRE.js",
// ...
"makeRequests.js"
]
}]
}
Performing the following after an onClick does not:
function executeNextScript(tabId, files, callback) {
chrome.tabs.executeScript(tabId, {
file: files.pop()
}, function () {
if (files.length)
executeNextScript(tabId, files, callback);
else
callback();
});
}
function executeScripts(tabId, callback) {
var files = [
"closure/goog/base.js",
"closure/goog/deps.js",
"util.js",
// ...
"makeRequests.js"
];
executeNextScript(tabId, files.reverse(), callback);
}
You can use the debugger JavaScript keyword to set breakpoints in your code.
I add //# sourceURL=myscript.js to any script that is injected and that adds it to the list of sources once it has been injected

Resources