As per the Wikipedia article on Bookmarklets (http://en.wikipedia.org/wiki/Bookmarklet), the concept of Bookmarklets is:
Web browsers use URIs for the href attribute of the tag and for
bookmarks. The URI scheme, such as http:, file:, or ftp:, specifies
the protocol and the format for the rest of the string. Browsers also
implement a prefix javascript: that to a parser is just like any other
URI. Internally, the browser sees that the specified protocol is
javascript, treats the rest of the string as a JavaScript application
which is then executed, and uses the resulting string as the new
page.
It says that the resulting string is used as the new page. So does that mean the original DOM that browser has is not affected by that string? But then how can I change or inject new DOM elements in the existing DOM if only the resulting string is used as a new page? Because script to alert Hello or to inject some new DOM element doesn't really return anything. They kinda work on the existing DOM.
Now, in internet explorer, apart from using Bookmarklets to execute some JavaScript on the page, I can write a BHO plugin and inject it in the following way:
document = (HTMLDocument)webBrowser.Document;
var injectedJS = System.IO.File.ReadAllText("InjectedJS.js");
var window = document.parentWindow;
window.execScript("(function(){ " + injectedJS + " })()");
Similarly in chrome, I can write an extension to achieve the same thing:
var s = document.createElement('script');
s.src = chrome.extension.getURL("script.js");
s.onload = function() {
this.parentNode.removeChild(this);
};
(document.head||document.documentElement).appendChild(s);
How are these different?
The high level questions that I have are:
Do these three approaches execute the JavaScript code in different environments?
Is there any limitation to one of them that another one doesn't have?
Is there any difference in the way the result of the execution is presented to the user or is reflected back in browser?
Is there any difference at all between the terms "JavaScript Injection" and "Bookmarklets"? Though I believe JavaScript Injection is an effect and Bookmarklets are a way to achieve that, BHO and Chrome extensions being another.
If assumption in 4 is correct, is there any difference in the way JavaScript is executed using BHO's execScript method or using javascript: protocol in a browser?
1. Do these three approaches execute the javascript code in different environments?
All of these three methods execute JavaScript code in the context of the page. When these methods are compared with each other, you can say that the JavaScript code is executed in the same environment.
Content scripts (Chrome/Opera/Firefox/Safari) run in an environment that is isolated from the web page, so looking from that perspective, the environment is indeed different.
BHOs are a tad different, because unlike the other extension platforms, the extension's language is not JavaScript, but C++, C#, ... The JavaScript code cannot directly access the BHO's native code (unless you implement such a thing yourself), so the environment is certainly "different".
2. Is there any limitation to one of them that another one doesn't have?
Bookmarklets are javascript:... URLs, and nothing more. Browser extensions can directly perform cross-origin HTTP requests, access persistent site-independent storage, etc. If you want to get similar features in a bookmarklet, you need to use an external web service.
Bookmarklets can only be active when they are manually activated by a user. Whether this is an advantage or disadvantage depends on your situation.
The maximum size of a bookmarklet is restricted by the maximum URL length, which is rather small. This limitation can be circumvented by inserting a <script src> tag in the document. The script has to be loaded first, so the execution of the code is delayed.
Bookmarklets can be used in almost every web browser, including the ones on phones and tablets (Chrome extensions can only be used in, well, desktop Chromium browsers).
3. Is there any difference in the way the result of the execution is presented to the user or is reflected back in browser?
No. In all cases, you're running code in the context of the current page. In theory, a page can replace all built-in methods (e.g. Function.prototype.call, String.prototype.replace, ..), and interfere or abuse the functionality of your script.
Possibly worth noting: The Crossrider and Kango extension frameworks implement the "content script" feature for Internet Explorer in a way that is similar to these three methods. This implies that pages can be crafted in such a way that they detect IE plugins written using these frameworks, intercept the API declaration and abuse their features.
4. Is there any difference at all between the terms "Javascript Injection" and "Bookmarklets"? Though I believe Javascript Injection is an effect and Bookmarklets are a way to achieve that, BHO and Chrome extensions being another.
There is no conceptual difference between a bookmarklet and "injected script". There are some practical differences, explained at section 2 of this answer.
(by "injected script", I assume you're referring to this method I coined the term to distinguish between the types of scripts in the Chrome extensions. Opera 12- and Safari both use this term for "content scripts").
5. If assumption in 4 is correct, is there any difference in the way javascript is executed using BHO's execScript method or using javascript: protocol in a browser?
Besides the previously mentioned differences, no.
Related
I'm building a project that allows users to add and annotate multiple <textarea> inputs with small javascript code snippets and run them individually using browser's javascript runtime. Imagine it to be similar to jupyter notebooks for client-side javascript.
However, I'm facing issues trying to share the same javascript execution context between the two snippets.
Imagine the first snippet to be:
let x = 5;
And the second snippet to be:
let y = x + 5;
I run the individual snippets using eval. However, as expected the second snippet complaints of x being undefined because it's not found in the same function scope.
We don't have the same behaviour in browser's DevTools (Javascript Console or Scripts) and they continue to share the same global execution context.
Questions:
Is this even possible to do using just javascript?
The browser is able to share global execution context in its Developer Tools Console. Are there any APIs that can utilise this functionality? Is the browser able to do so because it uses some native functionalities of the browser?
Is this even possible to do using just javascript?
Yes, something like that is possible, but I really wouldn't recommend that approach. Rather create an <iframe> and inject global <script> tags inside it, or get a web worker to run the notebook code - which also has the advantage that you can kill and restart it when it hangs.
The browser is able to share global execution context in its Developer Tools Console. Are there any APIs that can utilise this functionality? Is the browser able to do so because it uses some native functionalities of the browser?
Many tools can access that browser API, e.g. debugger integrated in IDEs. But yes, this is a native browser API, and it is not available from the javascript running inside a page.
There are some APIs which are still a bit mysterious to me. I'm not really sure if they represent a compatibility problem, but often I find tutorials online where they can access these variables/objects by default, and I simply cannot reproduce them locally, even after trying it on different browsers, environments and languages.
One of these is the browser variable. When and how can I access it?
For example, MDN's manifest.json documentation shows a very simple way for an extension to access typed shortcuts from the browser:
browser.commands.onCommand.addListener(function (command) {
if (command === "toggle-feature") {
console.log("Toggling the feature!");
}
});
It even shows an example here.
But I just can't access this browser variable/object anywhere. I've tried, with no success, doing so through JS, TS, Dart, both in website and extension environments; and the browser console on Chrome, Microsoft Edge and Firefox. How and when can I access it? — the chrome variable is also another one that seems inconsistent to me, sometimes I can access it, sometimes I simply cannot.
To directly answer your question: The browser object is only available when you develop a brower extension, not a web application. Therefore, what you ask for is not possible (access browser object from web page).
The longer answer is that the availability of infrastructure depends on the execution context: Is it a web page? Is it an extension? Or Javascript running on a server? Firefox? Chrome? etc. Unfortunately, lots of information on the internet assumes that it is "obvious" which execution context they are referring to.
In Chrome you have access to the chrome object on a web page. (you can check this on the console). Maybe that helps in your situation?
Last, regarding compatibility. Check out libraries like webextension-polyfill that unify behavior across browsers. But again, this only helps with browser extensions, not web applications.
unsafeWindow API was made so that userscripts could interact with the variables and functions of the pages the script executed on. However it is strongly discouraged as websites could then hijack userscripts through unsafeWindow and make them execute malicious code. However, why was unsafeWindow even necessary and why is it still used? Previously until Firefox 39, users were able to use the location hack as an alternative to unsafeWindow. This hack eventually stopped working due to an update in Firefox 39 which patched this. Despite this, users could still use GM APIs in the isolated sandbox and insert code through a script tag like this:
const fnToRunOnNativePage = () => {
console.log('fnToRunOnNativePage');
};
const script = document.body.appendChild(document.createElement('script'));
script.textContent = '(' + fnToRunOnNativePage.toString() + ')();';
// to use information inside the function that was retrieved elsewhere in the script,
// pass arguments above
script.remove();
I got this code from this stackoverflow post: How do I make my userscript execute code in isolated sandbox and unsafeWindow too?
So why is unsafeWindow is still useable? The code above is an almost perfect alternative to unsafeWindow. Also as a side note, is there any difference in the way unsafeWindow runs in Greasemonkey and Tampermonkey? Thanks.
External Resources:
https://sourceforge.net/p/greasemonkey/wiki/unsafeWindow/
https://www.tampermonkey.net/documentation.php#unsafeWindow
https://wiki.greasespot.net/UnsafeWindow
Let's call the creation of a <script> tag and inserting it into the page script injection.
Script injection has some drawbacks. To quote Brock Adams:
The script, at least the injected parts, cannot use the enhanced privileges (especially cross-domain) provided by the GM_ functions -- especially GM_xmlhttpRequest().
Can cause side effects or conflicts with the page's JS.
Using external libraries introduces even more conflicts and timing issues. It's nowhere near as easy as #require.
#require, also runs the external JS from a local copy -- speeding execution and all but eliminating reliance on an external server.
The page can see, use, change, or block the script.
Requires JS to be enabled. Firefox Greasemonkey, especially, can run on a page which has JS blocked. This can be godsend on bloated, crappy, and/or intrusive pages.
So, some developers may prefer to use unsafeWindow than to use script injection. Removing unsafeWindow would make things harder for them.
unsafeWindow often just works, and it can be less cumbersome than the creation and injection of a script tag.
Another issue is that, on the web, backwards compatibility is often one of the most important factors that is rarely, if ever, given up. If something works in a 2015 version of something, the general philosophy is that it should work in the 2020 version too, without requiring whoever worked on the 2015 version to come back and fix it (since they may not be around to do so anymore). Related discussion here. While userscript managers don't have to care as much about backwards compatibility as other things on the web, the same sort of reasoning applies - avoid breaking things that are currently working unless you have a really good reason.
In addition to the excellent explanation by CertainPerformance, unsafeWindow is simply implemented as an alias of window.wrappedJSObject.
As a script-manager developer myself, initially, I resisted the implementation of unsafeWindow but later had to add it for compatibility.
Modern browsers separate the JavaScript injected by the extension from the web page JavaScript, for security reasons.
Firefox goes further with a dedicated userScripts API to further limit the access of userscripts (i.e. 3rd party scripts) in comparison to extension's own scripts.
Bridging between these separated scopes/contexts (page, extension, userscript) increases the security concerns.
However, such interaction is sometimes needed to overwrite (undesirable) page JavaScript which can be a good thing, or to get data that is stored in the page JavaScript.
In conclusion, although unsafeWindow creates the possibility of unsafe bridges, it is not a new bridge and the same access is already available with window.wrappedJSObject.
Is there anything different in what you can do with eval v.s. what you can do in the browser developer console? is it not safer to use eval, as at least, your code evaluates the user input in a certain context, and, it can also log (and scan) the input prior to execution....
Browser console and eval() are two different things...
in my opinion can't be compared just like that.
Browser console its built into browser (and as it, it's browser specific javascript interpreter)
Lets you execute code besides many other things.
firefox The Web Console: Logs information associated with a web page: any network requests,
JavaScript, CSS, security errors and warnings as well as error,
warning and informational messages explicitly logged by JavaScript
code running in the page context Enables you to interact with a web
page by executing JavaScript expressions in the context of the page.
*The Browser Console is like the Web Console, but applied to the whole
browser rather than a single content tab.
google-chrome The Chrome DevTools Console panel is your focal point for direct
interaction with a page in real time. The Console lets you use
standard JavaScript statements and Console-specific commands while a
page is live in the browser to help you debug the page. View
diagnostic messages, display both raw and structured data, control and
filter output, examine and modify page elements, measure execution
time, and more.
eval() its a javascript method
The eval() method evaluates JavaScript code represented as a string.
So yes, there's a BIG difference between them and what you can do...
...but most important is how you do it.
Now, you ask about security implications with both "options", but I think this is too ambiguous in the way you pose the question, could be user specific answer depending on how we interpret about what you're trying to clarify..
I believe you will need to clarify/elaborate a little more your question and give us some real examples of what you're after.
or maybe not, and this mini explanation is enough to clarify your doubt
I guess using eval within your code runs in the context where eval is being called in your code, whereas the developer console can only access globals, and hence can only access your code-as-written but not necessarily live data created by its execution, but am not sure about that nor about loopholes.
Is it possible from Inside the Chrome Browser to connect to the Remote Debugging Protocol? - without installing and creating extension for that purpose.
The purpose would be to test a JavaScript code created inside the HTML page using ACE editor or similar, to allow user to run code snippet within the page and then return the result to the calling page. For example, the code might be running inside an IFRAME.
At least http://brackets.io/ is said to "Brackets is a web-based IDE that uses the Chrome debugging protocol to enable debugging and live HTML/CSS development." - which lets me wonder, is there client JS API for browser to connect with WebSockets to the interface or do you have to write that interface by yourself?
So, there seems to be several options for client, but what about the browser itself?
EDIT: assuming here that the browser was started with --remote-debugging-port=... set to a meaningful value.
Not directly. As far as I can tell, the remote debugging interface is only available if it has explicitly been enabled at startup using the --remote-debugging-port= command-line flag. There doesn't appear to be any way to activate it at runtime; even if it were, you wouldn't be able to access it from a web page.
Keep in mind that Brackets is a standalone application based on Chrome; it doesn't run as a web site. As such, it can do some things that aren't possible in a browser.
Now, that all being said, there may be a way to make some error reporting and debugging features available if you're careful. In particular, if you can inject code into your iframe, you could attach an event handler to the global onerror event to catch exceptions. You may need to use some special tricks to pass events from the frame to the parent page — Window.postMessage may be helpful here — but that should at least get you started.