Execute javascript retrieved with AJAX in Chrome extension - javascript

I am writing a chrome extension that injects a div into a website with a content script. The content script makes an AJAX request to a website that I cleared in the manifest.json file and it inserts the data into the div with innerHTML. Part of what the AJAX request returns is javascript that needs to be executed. The AJAX request from within the content script works fine.
When I make the same AJAX request from a regular website, the javascript that is returned executes just fine, but when I make the AJAX request from the content script it does not execute. No errors are displayed in the console. I don't want to reload the website, if possible.
I assume that this is a security 'feature' and not a bug. How can I turn off or circumvent this behavior?

First off what Rob W said is very important, if you don't already know it, a good explanation of the different environment a content script runs in is useful.
You might want to check this out. It's not 100% what you're looking for but the main part is there. Basically from your background page (if you don't have one already create one), you use chrome.tabs.executeScript() to execute the script you've downloaded. That runs the javascript in the real page context instead of the "content script" context. All you need now is to get that script (in string form) to the background page, and determine the tabId to execute it on (from the sender tab)
You can use chrome.extension.sendMessage to send it to the background page, and in the background.js, use chrome.extension.onMessage to receive the message with your script. From there use the sender argument to get the tabId (sender.tab.id), and build your executeScript call.
One more helpful hint, page scripts (dynamic javascript executions) in chrome by default don't show up in any set way in the chrome debugger, but you can append something like this to the string of your javascript:
"\n//# sourceURL=/myFolder/myDynamicJavascript.js"
This will make this script always show up with the "/myFolder/myDynamicJavascript.js" path for the chrome debugger, allowing you to set breakpoints in the js code you've inserted. It's a lifesaver.

Related

Wistia E-v1.js script being loaded twice

So I am calling the wistia script with a script tag in my head like this:
<script charSet='ISO-8859-1' src='//fast.wistia.com/assets/external/E-v1.js' async defer data-script='wistia' />
However, when I check out the network tab on Chrome, I notice that the E-v1.js script from Wistia is being loaded twice, which is rather significant as it is a 273kb script.
The first load of the script is from https://fast.wistia.com/assets/external/E-v1.js, the location to which I have called it.
However, the second load of the script comes from an iframe, despite me not having put any iframes on the page. This iframe calls the script even on webpages which do not contain any wistia videos. The referrer is: https://fast.wistia.com/embed/iframe_shim?domain=com.
What's going on here? I assume this is some trying-to-be-helpful behaviour from wistia to lazy load their script via an iframe, but it's already loaded...
So I contacted Wistia and got an answer. Their development practices are not exactly intuitive.
Here's what the guy said:
The iframe_shim is a way of tracking the visitor_key for stats tracking, and storing that information on the fast.wistia domain rather than your domain. For a more lightweight method of doing that, you can set window.wistiaIframeShim = false in script tags on your page, and that will stop E-v1.js from loading again. Visitors will then be tracked via a cookie and localstorage directly on your domain instead of the fast.wistia.com domain. As far as I know this shouldn't be problematic, and we'll eventually be changing how that works to make it more efficient, it just hasn't been prioritized yet.
So they seem to load it twice from two different origins just to store a tiny amount of information on their own domain rather than on the client. Seems ridicuous to me, but I can confirm as of right now that all you have to do is change that window variable.
THE FIX: window.wistiaIframeShim = false

Is there an alternative to preprocessorScript for Chrome DevTools extensions?

I want to create a custom profiler for Javascript as a Chrome DevTools Extension. To do so, I'd have to instrument all Javascript code of a website (parse to AST, inject hooks, generate new source). This should've been easily possible using chrome.devtools.inspectedWindow.reload() and its parameter preprocessorScript described here: https://developer.chrome.com/extensions/devtools_inspectedWindow.
Unfortunately, this feature has been removed (https://bugs.chromium.org/p/chromium/issues/detail?id=438626) because nobody was using it.
Do you know of any other way I could achieve the same thing with a Chrome Extension? Is there any other way I can replace an incoming Javascript source with a changed version? This question is very specific to Chrome Extensions (and maybe extensions to other browsers), I'm asking this as a last resort before going a different route (e.g. dedicated app).
Use the Chrome Debugging Protocol.
First, use DOMDebugger.setInstrumentationBreakpoint with eventName: "scriptFirstStatement" as a parameter to add a break-point to the first statement of each script.
Second, in the Debugger Domain, there is an event called scriptParsed. Listen to it and if called, use Debugger.setScriptSource to change the source.
Finally, call Debugger.resume each time after you edited a source file with setScriptSource.
Example in semi-pseudo-code:
// Prevent code being executed
cdp.sendCommand("DOMDebugger.setInstrumentationBreakpoint", {
eventName: "scriptFirstStatement"
});
// Enable Debugger domain to receive its events
cdp.sendCommand("Debugger.enable");
cdp.addListener("message", (event, method, params) => {
// Script is ready to be edited
if (method === "Debugger.scriptParsed") {
cdp.sendCommand("Debugger.setScriptSource", {
scriptId: params.scriptId,
scriptSource: `console.log("edited script ${params.url}");`
}, (err, msg) => {
// After editing, resume code execution.
cdg.sendCommand("Debugger.resume");
});
}
});
The implementation above is not ideal. It should probably listen to the breakpoint event, get to the script using the associated event data, edit the script and then resume. Listening to scriptParsed and then resuming the debugger are two things that shouldn't be together, it could create problems. It makes for a simpler example, though.
On HTTP you can use the chrome.webRequest API to redirect requests for JS code to data URLs containing the processed JavaScript code.
However, this won't work for inline script tags. It also won't work on HTTPS, since the data URLs are considered unsafe. And data URLs are can't be longer than 2MB in Chrome, so you won't be able to redirect to large JS files.
If the exact order of execution of each script isn't important you could cancel the script requests and then later send a message with the script content to the page. This would make it work on HTTPS.
To address both issues you could redirect the HTML page itself to a data URL, in order to gain more control. That has a few negative consequences though:
Can't reload page because URL is fixed to data URL
Need to add or update <base> tag to make sure stylesheet/image URLs go to the correct URL
Breaks ajax requests that require cookies/authentication (not sure if this can be fixed)
No support for localStorage on data URLs
Not sure if this works: in order to fix #1 and #4 you could consider setting up an HTML page within your Chrome extension and then using that as the base page instead of a data URL.
Another idea that may or may not work: Use chrome.debugger to modify the source code.

Manipulate website which has been called from another website with Greasmonkey [duplicate]

This question already has answers here:
Fire Greasemonkey script on AJAX request
(2 answers)
Closed 7 years ago.
I have the following situation:
A website gets data from another html-file("news.html") which gets called every 10s or so
I want to manipulate the data from the news.html
I thought I could set up a greasemonkey script which manipulates the news.html and thus also the main website.
However this assumption was wrong: When I open the news.html in my browser, the news are manipulated (in terms of data - just to clarify this), but when I visit the main website the news don't get manipulated.
I think that greasemonkey does not work when the website is not opened "directly" in the browser, but with ajax/jQuery/....
Is there any known workaround for this?
Thanks in advance!
You can't change files that are on a server with greasemonkey unless the API for some reason leaves that exposed. Whatever you are changing is just local to you.
For simple pages, it's safest to wrap your greasemonkey scripts in at least an unload handler: According to the "Authoring" page at http://greasemonkey.mozdev.org/authoring.html,
User scripts are executed after the DOM is fully loaded, but before
onload occurs. This means that your scripts can begin immediately and
don't need to wait for onload. However, replacing large parts of the
DOM (e.g. using innerHTML or outerHTML) at this early stage of
rendering is known to cause Firefox some trouble. In this case, you'll
have more success if you call your code in response to the load event
instead:
window.addEventListener("load", function(e) {
document.innerHTML = "Hello, world!";
}, false);
However, if the "main site" is constructing itself via a secondary ajax call to news.html, that won't be enough, because the data you want to manipulate won't be in the DOM yet when your script runs on the main site. You'll need to delay your script's execution until after the main site has finished doing its thing, so that when you try to do your thing there'll be the thing there for you to do your thing to. So to speak.
Have your script observe the DOM and wait to run until after news.html has been injected into the main site, or be lazy and start it after a sufficiently-long setTimeout.
(A clarification based on discussion in comments: Greasemonkey will only act on the site that was actually loaded in the browser; it will not act directly on every XHR request that site makes, even if that url was #included in the script. So if site "foo.com" ajax-injects content from "bar.com/news.html", and the browser loaded "foo.com", greasemonkey will not directly modify the "bar.com/news.html" request foo.com made; it can only work with the DOM that foo.com constructs based on what it got from news.html.)

Greasemonkey script works only after page reload [duplicate]

I am working on a Greasemonkey script to turn some text into links on a a Rally page. The script works fine only when I reload the page. If I navigate to the page in any manner (links, browser forward/back) the script does not run, despite the fact that the Greasemonkey menu shows my script at the bottom, with a checkmark.
Here is an example URL:
https://rally1.rallydev.com/#/4745909548/detail/userstory/6138899084/changesets
My matching rule:
/^https://.*\.rallydev\.com/.*/changesets$/
I don't know if the hash is causing a problem, but everything is fine when I reload.
Not sure where to go from here. Any help is appreciated.
It's impossible to be sure what's going on, because the target page(s) are behind a pay-wall and their alleged "Free Trial" mechanism blows chunks.
Here are some possible causes of the current behavior:
The initial request is insecure (http) but redirects to a secure page (https).
The first page load does a some other kind of redirect to the actual page.
The target content is in an <iframe> that does not load right away.
The target content is AJAXed-in.
Something exotic that we would need to see the actual page to figure out.
The initial URL does not really end in changesets.
Also, get into the habit of escaping the /s in the middle of regular expressions. It's not always needed, but it will eventually bite you in the [censored] if you don't.
So the script should use:
// #include /^https:\/\/.*\.rallydev\.com\/.*\/changesets$/
to start, but see below.
Steps to a solution:
Change your #include to account for http and the Possibility of trailing space or trailing slash in the URL. Use:
// #include /^https?:\/\/.*\.rallydev\.com\/.*\/changesets(?:\s|\/)*$/
Examine the page with Firebug. Is the content AJAXed-in? Is it in an <iframe>? If so, what is the iframe URL, if any?
To also detect AJAX and/or redirects, use Firebug's Net panel and/or Wireshark.
If possible, provide us with login credentials so that we may see a problematic page.
Snapshot a problematic page (Save it via Firefox) and link to that HTML and JS in Pastebin.com.
Consider using code like:
if (window.top != window.self) {
//--- Don't run on/in frames or iframes.
return;
}
To have the script run only in (or not in) iframes, as applicable.
If the problem is caused by AJAX delays (or loading of new content), get around that by using the waitForKeyElements() utility as shown in "Fire Greasemonkey script on AJAX request".

Can I use Javascript to get a web page Without the use of XMLHttpRequest or document object?

web server send HTML(1) ---> Browser get the HTML(1) ---> script engine starts to work and so on..
I need to get a HTML(1) in my project.
'XMLHttpRequest.responseText' can do it. I think it's great!
But it have to make an another request.
And 'document.getElementsByTagName()' return a rendered HTML. It's not that I want.
we can see the HTML(1) in 'script' tab on IE developer tool.(not 'html' tab)
Then I guess that it could be possible to get HTML(1) without another request.
Is there any way to get the HTML(1) with javascript?
Added-----------------------------
there is sample.html
&ltscript&gt
document.write('AAAAAA');
&lt/script&gt
&ltbody&gt
body content
&lt/body&gt
document.getElementsByTagName('html') return like following.
&ltscript&gt
document.write('AAAAAA');
&lt/script&gt
&ltbody&gt
AAAAAAbody content
&lt/body&gt
But I need... pure source of the web page
If you want to get the current page as a string use document.documentElement.innerHTML
I don't think browsers actually keep the original HTML which was sent by web server, because if you try to view a page source the browser actually makes a request to the server.

Categories