Webextension inline install chrome.runtime.connect issues - javascript

I'm having a really weird issue, I've developped a webextension that uses messaging between content script and background script (using chrome.runtime.connect) and nativemessaging.
The issue i'm facing is that when I install the extension (manually from the store beforehand and then connect to my website, everything works as expected, the chrome.runtime.connect works and returns a valid port to the background script.
But when i do an inline install of the extension from my website to get around the fact to have to navigate to have the content script in the webpage, i manually inject the content script into my page using
function injectContentScript() {
var s = document.createElement("script");
s.setAttribute("src", "chrome-extension://<extensionid>/content.js");
document.head.appendChild(s);
}
and the exact same content script but manually injected doesn't behave the same. chrome.runtime.connect returns a null object and chrome.runtime.lastError gives me
Could not establish connection. Receiving end does not exist.
I'm calling on the sender side (content.js - manually injected content script) chrome.runtime.connect(extensionID) where extension id is the id of the extension generated by the chrome webstore. And on the receiving side (background.js - extension background script) chrome.runtime.onConnect.addListener(onPortConnected);
I'm not really sure how to debug this issue, maybe it's a timing issue?
The background script is well executed even with the inline install (i've added logs and debugged it through the background.html in chrome extension manager)
Any help would be greatly appreciated!

You have two scenarios.
Your content script content.js is executed as normal upon navigation, as a content script defined in the manifest.
In this case, it executes in a special JS context attached to the page and reserved for your content scripts. See Execution Environment docs section for explanation. It is isolated from the webpage and is considered part of the extension (albeit with lower privileges).
When you connect from a content script, chrome.runtime.connect() is treated as internal communication between parts of the extension. So while you can provide the extension ID, it is not needed.
More importantly, the event raised in this case is chrome.runtime.onConnect.
Your supposed "inject content script immediately" code called from the webpage does something completely different.
Instead of creating a new execution context, the code is instead added directly to the page; it is not considered part of the extension and has no access to extension API.
Normally, a call to chrome.runtime.connect() would simply fail, as this is not a function exposed to webpages; however, you also declared externally_connectable, so it is exposed specifically to your webpage.
In this case, passing the extension ID is mandatory for the connect. You were doing this already, so the call was succeeding.
However, and that's what made it fail: the corresponding event is no longer onConnect, but onConnectExternal!
What you should be doing is:
Not mixing code that is run in very different contexts.
If you need communication from the webpage to background, always do it from the webpage, not sometimes-from-content-sometimes-from-page.
That way you only have to listen to onConnectExternal and it cuts out the need for a content script (if it was its only function).
See the docs as well: Sending messages from web pages.
You don't have to source the code from chrome-extension://<extensionid>/; you can directly add this to your website's code and potentially avoid web_accessible_resources.
And if you actually want to inject content scripts on first run, see for example this answer.
Related reading: How to properly handle chrome extension updates from content scripts

Related

How to prevent Google Tag Manager overwriting document.write()?

We are using Angular for our Website. As not all Pages have been ported to Angular, we implemented a hybrid approach:
Every request goes to Angular first. When it has been loaded, it checks if the Route exists
If not, the HTML-page is fetched from the backend
The html-Element in the DOM (i.e. the complete page) is replaced with the response's body
ngOnInit() {
this.railsService.fetchRailsPage(this.router.url).subscribe(
(response) => this.replaceDOM(response),
(errorResponse) => this.replaceDOM(errorResponse.error)
);
}
private replaceDOM(newContent: string) {
document.open();
document.write(newContent);
document.close();
}
Since all a-hrefs in old pages are plain old hrefs (not Angular's routerLinks), once the user navigates away, the page is reloaded and Angular kicks in again.
So far, it works, but: I noticed that sometimes the DOM is not replaced with the response body.
Debugging brought us to the conclusion that Google Tag Manager could be the issue. It overwrites document.write() and a lot of other default Javascript functions.
Why is that? And how can this be prevented to get the default version of e.g. document.write()?
Seconding Alan here.
Please make sure you're running two tests:
Block gtm with the request blocking function of the dev tools and try reproducing the issue.
Try creating an empty GTM container, loading it on page and reproduce the issue.
If the first test shows that The issue persists with GTM blocked, then it's not GTM.
If the second test shows that the issue is solved, then it's not about GTM but about the logic used in it's configuration.
If anything, I would first make sure no custom code in GTM additionaly overrides document.write (which I've never seen before, but it's definitely possible). Then I would broadly audit all custom scripts deployed by GTM. After that, I would try pausing all the element visibility triggers if any are deployed and seeing if that helps.
GTM likely would aim to override write to be able to watch DOM changes. But it does so gently, adding a bit of tracking there and not changing the essence of it. It's severely unlikely that GTM's core logic would conflict with Angular.
//UPD just had a chat with a colleague on Measure. It looks like the only scenario when GTM overrides the document.write is when there are Custom HTML tags that have an option to "support document.write". The Element Visibility trigger uses mutation and intersection observers rather than listening to document.writes.

Replacing a jQuery script using a Chrome Extension

A website is running a jQuery script. I want to use a Chrome Extension to have the site run my own version of the jQuery script, and not the normal one.
So far, I've managed to make the chrome extension find where the website calls the normal script, and I've replaced it with my own:
document.querySelector("script[src^='website.com/originaljqueryscript']").src = 'myjqueryscript';
As a test, I made myjqueryscript the exact same script as the originaljqueryscript. I set the run_at in the manifest to run at document_end.
When I try to open the website with my script enabled, the console logged an error $(...).dialog is not a function. I think this is because jQuery is not loaded in my chrome extension. So then I found which version of jQuery the website is using, and added that to my chrome extension. Now I get this error: $(…).dialog is not a function I believe that error is due to a conflict between the two jQuerys that have been loaded (one on the website, one from my extension).
Am I on the right track, or is there a better way to replace a websites jQuery script with my own?
If this is for a very specific website, loading jQuery from a specific URL, you can use webRequest API to intercept the request to jQuery and redirect it to a file bundled in your extension. E.g.:
chrome.webRequest.onBeforeRequest.addListener(
function(details) { return {redirectUrl: chrome.runtime.getUrl("js/myjquery.js")}; },
{urls: ["https://example.com/js/jquery.js"]},
["blocking"]
);
(Note: this sample is very minimal; you may need to include and inspect request headers to make sure that the source page is your target site - you really don't want to replace a CDN-provided jQuery for all sites)
This assumes that the website does not use Subresource Integrity checks, however I believe that it will bypass a script-src Content Security Policy (redirect is transparent).
Note that .dialog() is part of jQuery UI, not core jQuery; the site presumably loads both, and you'll need to intercept both. It's possible that the site actually bundles them together.

Firefox add-on declaring functions and use in content script

i am trying to write my first firefox add-on. the main problem seem s to be that i am also new to javascript. at the moment i have:
require('sdk/page-mod').PageMod({
include: ["*"],
contentScript: 'window.addEventListener("click", function(e) { alert("blub"); }, false);',
attachTo: ["existing", "top"]
});
(thx to the answer here.)
now i want to use a declared function instead of an anonymous one, but i cant get it to work:
require('sdk/page-mod').PageMod({
include: ["*"],
contentScript: 'window.addEventListener("click", function(e) { alert("blub"); }, false);',
attachTo: ["existing", "top"]
});
getImgData function (e) {
alert("blubber3");
}
the first problem is i get syntax error by just adding the function "missing ; before statement". But cfx doesn't tell me the wrong line. (Is there any useful tool for js editing with good syntax check/ content assist?)
So how to declare a function and use ist somewhere else in the script. At the end the function needs to get the target of click and parse it.
(i read the tutorials but thy all use anonymous functions :-P)
thx in advance
It's important to realize the separation between chrome scripts and content scripts. Chrome scripts are those that run with the same security privileges as Firefox - they have full access to Firefox and your computer. Content scripts are those that run with the same privileges as web pages. They can mess around with that web page, but are severely restricted otherwise. To maintain security, the way these two types of scripts can communicate is limited. You wouldn't want a web page to be able to call any function it wants in your extension's internal code!
Your main JS file (the one that includes require('sdk/page-mod')) is a chrome script. What you're injecting (contentScript) is (obviously) a content script. They can't communicate through a direct function call as you're doing.
If your getImgData function is something that can be done with normal web page privileges, you can move your definition of it to within the content script. If it requires additional privileges, you must have your content script communicate with your chrome script via the emit and on functions as described in the link above.
If you are going to make your content script any longer, I would recommend you separate it into its own file to make your life easier.

Javascript in asp.net MVC... Beginner issue

I created an Asp.Net MVC Internet Aplication and in my Index view of the Home Controller I have this
This is the first line, before the script results.
<script type="text/javascript" src="~/Script/Teste.js"></script>
<br />
This line comes after the script.
In my Teste.js I have this:
document.write("Yes! I am now a JavaScript coder!");
But nothing happens. If I change the src attribute and put some random name src="aaaa", despite the fact "aaaa" doesnt exist, I get no error in runtime.
EDIT
Also, check your path again. The default MVC templates in VS create a folder called Scripts, not Script. ("~/Scripts/teste.js")
Per the comment below, this was not the root cause of the issue, but in other cases can easily bite new JavaScript developers.
Most likely, your document.write function is firing before the document is ready, leading to the appearance that nothing is happening. Try the following in your Teste.js file
window.onload = function ()
{
document.write("Yes! I am now a JavaScript coder!");
//or even better as a test
alert("This alert was called");
}
Check the source of your page as well, it could be the document is being written to, you just can't see it due to markup/page styling.
As for you second issue, there will be no 'Runtime Exception' thrown if you reference a non-existent file. If you are using tools like Firebug or Chrome's developer tools, you should see a request to http://siteDomain/Scripts/aaaa.js with a response of 404, not found.
You generally should avoid using document.write() unless you absolutely have to use it for some reason... I don't think I've ever come across such a situation, and write a lot of Javascript.
Try this:
1) Put this in your HTML:
<script src="/scripts/teste.js"></script>
2) Put this in your JS:
alert('Yes! I am now a JavaScript coder!');
3) Open Chrome since it makes it easy to look for external resources loading and open the Network tab in Developer Tools (click the menu button at top-right, Tools > Developer Tools, Network tab).
4) Run your project and copy/paste the URL in the browser that comes up into this Chrome window, and hit enter.
When your page loads one of 2 things will happen:
A) You'll get the alert box you wanted or
B) You'll find out why it isn't loading because the Network tab will show the browser attempting to fetch teste.js and failing in some fashion, for example a 404, which would indicate you've got a typo in the path, or the script isn't where you thought it was, etc.
Put the following line at the very end of your document. There should not be anything after. Then try to load the page.
<script type="text/javascript" src="~/Script/Teste.js"></script>
Also, try pressing F12 once the page loads to see the source. Check if you script is there.
In MVC, the tilde is used to refer to the root URL of your application. However, it cannot normally parse this information. If you write:
<script src="~/Script/Teste.js"></script>
The lookup will fail, because the ~ means nothing special in HTML. If you're using Razor as your view engine (not ASPX), you need to wrap that call in Url.Content like so:
<script src="#Url.Content(~/Script/Teste.js)"></script>
Doing this will ensure a valid URL is provided to the browser.
With that in mind, you need to check that you have the file name and folder name both correct. You also need to ensure that the file is being deployed with your application. You can do this my opening the properties panel while the file is selected in the Solution Explorer and pressing F4.

Google Chrome extension run as if in console?

I'm trying to make a Google Chrome extension that pauses/plays the payer in DEEZER (www.deezer.com). I i manually run the code "playercontrol.doAction('next')" in the Google chrome JavaScript console, I can manipulate the deezer player, so i looked at the possibility of injecting the code into the Deezer web page with an extension but I haven't managed to do so successfully.
Is it possible to run JavaScript code in Deezer web page as if it were coming from the console in Google Chrome?
If so how?
The problems that you are facing here are the following:
You need to use content_scripts instead of running code from the background page.
Code run from the background page and code run as content scripts are "Sandboxed". This means that your code doesn't have access to the global variables created by the page. Your extension doesn't have access to it. THE GOOD NEWS IS that you can still do it. You have to be clever, though.
In your extension, you have to inject a new Script tag into the "head" of the page. You have to create a new script tag and append it to the head of the page. Before you append it, you need to set the "innerHTML" of the script tag to be the JavaScript that you want to run. Here is the gist of what you need to do.
var myJavaScript = "alert('hey guys');"; //You need to put your JS here.
var scriptTag = document.createElement("script");
scriptTag.innerHTML = myJavaScript;
document.head.appendChild(scriptTag);
This will put the new script tag into the head, which bypasses the security of the Chrome Extension. This will give the new script access to the regular global variables on the page, ie: you will have access to the 'playercontrol' object.
This feature has been deprecated by Chrome's security policy.

Categories

Resources