I am trying to write a small script to copy in the web console of a live page, and part of what this script needs to do is take several screenshots of the document body. This script will be for personal use, for a very specific task, so I think it would not be a problem to use Firefox's built in helper function :screenshot, instead of a more cross-compatible solution.
I have read this question about the same topic, which explains why it is not possible to call such helper functions from the webpage's console in JavaScript. But what I thought I could do instead, is to use Firefox's browser console, which gives access to the entire browser. Again, I have been literaly just copying and pasting functions into the console to use while interacting with the page, so if I can call the :screenshot function programmatically from the browser console I would just need to figure out how to access the DOM of a particular document or tab and I will get the same result.
I have tried to import and use html2canvas, but it did not work most probably because the content that I am trying to screenshot resides inside a shadow-root. I know that one alternative is to write my own extension, but I would like to avoid such a job for this task. Eventually, do you know if it is possible to achieve similar results in a Chromium based browser (Brave)?
Thank you very much :D
The good news is that, yes, you can invoke the devtools screenshot functionality from the browser console. Conveniently the default selected node is the document body.
(async()=>{
const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm");
const { gDevTools } = require("devtools/client/framework/devtools");
const { TargetFactory } = require("devtools/client/framework/target");
const target = await TargetFactory.forTab(gBrowser.selectedTab);
const toolbox = await gDevTools.showToolbox(target, "inspector");
const inspector = toolbox.getPanel("inspector");
inspector.screenshotNode();
})()
Now the bad news. Accessing the DOM of content page from the browser console is unbearably tricky :(
You are going about solving the problem in the wrong manner. Firefox since version 57 has built in tools to provide what you want. To accomplish this you want to use Firefox's headless mode with Webdriver if needed.
The simple example from MDN is
/path/to/firefox -P my-profile -headless --screenshot https://developer.mozilla.org/
Related
I want to know from within javascript in a webpage if any chrome extension installed in Chrome is reading the DOM. I want to be able to do this even if the extension does not insert any HTML or script into the DOM. Is there some way I can do this? Basically I want to prevent scraping of the website content.
I see this option
http://blog.kotowicz.net/2012/02/intro-to-chrome-addons-hacking.html
but this requires that I know the ids of the extension. I want to be able to do this more natively across all extensions that are installed.
Is there some event I can get based on when a DOM element is ready or some DOM function is called?
Thanks in advance for your help.
There are lots of methods for reading the DOM, so catching everything would be difficult. However, one approach you can do is wrap individual DOM querying methods. For example:
const origFn = HTMLDocument.prototype.querySelector;
HTMLDocument.prototype.querySelector = function(...args) {
const error = new Error();
console.log(`querySelector was called. Args:`, args, ', Stack trace', error.stack);
return orig.apply(this, args);
}
This will do a console.log whenever document.querySelector is called. I'm not 100% sure if the stack trace will reference Chrome extension JavaScript, but it's worth a shot. Also, it's not guaranteed that all prototypes will be overwriteable like this.
I want to read a value of DOM element. I am new to browser extensions. I came across codes which provided help in executing a code in browser context but how do I fetch a value and use in extension's context?
var value = document.getElementById('id1'); // Here document should be of browser context.
Try
var value = document.getElementById('id1').value;
// note the extra .value.
You have to access the DOM from content scripts.
If you are looking to get the currently active tab, you would do this: https://developer.chrome.com/extensions/activeTab
How this will work will depend on whether you are in browser context, content scripts or background though.
I highly recommend reading the chrome extension guides: https://developer.chrome.com/extensions/getstarted. They help quite a bit, and the different scopes of the chrome extension are crucial to understand to successfully develop on it.
In my add-on, I'm using XUL to display dialog windows because I can customize their appearance to suit the add-on's general style (like a custom titlebar).
Using the migration guide, I'm able to do this easily. The thing is, I would like to call certain functions in the add-on's main module from the XUL dialog.
After a bit of searching I found the loader module, which seems to be able to do exactly what I want. But, I'm experiencing trouble in using it to access the main module.
First, I tried the obvious approach as mentioned in the documentation;
xul_dialog.js:
let {Loader} = Components.utils.import('resource://gre/modules/commonjs/toolkit/loader.js');
let loader = Loader.Loader({
paths: {
'toolkit/': 'resource://gre/modules/commonjs/toolkit/',
'': 'resource:///modules/',
'./': 'resource://<my-addon-name>/root/'
}
});
let main = Loader.main(loader, './main');
I got an error that './main' was not found at resource://<my-addon-name>/root/. Figuring that I was using the incorrect paths, I experimented a bit until I could remove all path associated errors.
xul_dialog.js:
let {Loader} = Components.utils.import('resource://gre/modules/commonjs/toolkit/loader.js');
let loader = Loader.Loader({
paths: {
'toolkit/': 'resource://gre/modules/commonjs/toolkit/',
'': 'resource://gre/modules/commonjs/',
'./': 'resource://<my-addon-id>-at-jetpack/<my-addon-name>/lib/'
}
});
let main = Loader.main(loader, './main');
This time I got a rather confusing error at loader.js, line 279.
Components is not available in this context.
Functionality provided by Components may be available in an SDK
module: https://jetpack.mozillalabs.com/sdk/latest/docs/
However, if you still need to import Components, you may use the
`chrome` module's properties for shortcuts to Component properties:
Shortcuts:
Cc = Components.classes
Ci = Components.interfaces
Cu = Components.utils
CC = Components.Constructor
Example:
let { Cc, Ci } = require('chrome');
I get the same error when I use Loader.Require(loader, {id: './main'}) instead of Loader.main. I even tried passing Components as globals when instantiating the loader, but without much luck.
I'm fairly certain that I'm doing a lot of things wrong. I don't understand why I'm getting the error, even after spending quite a bit of time in loader.js. Plus, I also think that there would be a better alternative than having to use the add-on id for the path to main.js; hard-coding it like that doesn't seem right.
Any help would be really appreciated.
What you have to do is to find a specific instance of Loader, not create a new one.
At main.js
const { id, name, prefixURI } = require("#loader/options");
//pass these to the XUL dialog
At xul.js (or whatever is the name of the xul dialog script)
Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm");
var extensionscope = XPIProvider.bootstrapScopes[id];
var mainjssandbox = extensionscope.loader.sandboxes[prefixURI + name + "/lib/main.js"];
Assuming there is a foo function at main.js, you can call it like
mainjssandbox.foo();
Of course don't expect things to work as if XUL and Add-on SDK actually blended into one thing.
If it is your XUL dialog that should interact with your add-on, then please don't use the Loader stuff and in particular do not go the XPIProvider.bootstrapScopes #paa suggested. While this might work (for now), it should be noted that it relies on tons of implementation details that are subject to change at any point making this solution extremely fragile.
Instead there are a couple of other options (not an exhaustive list):
If the SDK part opens the windows, you may use .openDialog which supports passing arguments to the created window, and these arguments can even be objects and functions. Also, you can have the window dispatch (custom) events, and which your SDK part can listen to by calling addEventListener on the window the .openDialog call returns.
If the window is created from somewhere else (e.g. from the AddonManager because of em:optionsURL) then the nsIObserverService is another way to communicate. The window could e.g. .notifyObservers on DOMContentLoaded containing a reference to itself. The SDK parts would just have to observe such notifications by addObserver.
Another way, a bit hacky but working, is the SDK part listening to new windows via nsIWindowWatcher.registerNotification and then injecting some API into e.g. browser.xul windows by XPCNativeWrapper.unwrap(subject.QueryInterface(Ci.nsIDOMWindow)).myAddonAPI = something.
Be sure to handle unloading or your add-on well - it is still restartless -, i.e. reverse any changes you've done and remove any observers and so on again.
If you want to interact with SDK addons not created by you, then the XPIProvider route might be the only feasible. But still, it might be worth contacting the add-on author first asking for the addition of some public API instead of hacking deep into the AddonManager and SDK loader internals.
PS:
Considering passing require or the global scope to your window via openDialog if you want to. Get the global scope by placing this into your main.js:
const globalScope = this;
I made a function called test() in javascript file.Placed a simple alert into it.
In html file, called the method on click of a button. But,it was not being invoked.
Problem was in the 11th function, nowhere related to mine !!!! But, how can a person making his first javascript function suppose to find that out ???
I am looking for best ways to debug javascript.
You can debug javascript using many modern browsers. See this question for details on how to debug in Google Chrome:
How do you launch the JavaScript debugger in Google Chrome?
Furthermore, you shouldn't use alert() for debugging as this can give different results to a production version due to alert() causing a pause in the script.
It is best practice to use console.log() and view the output in the browsers Console.
You can also put debugger in your javascript code to force a breakpoint. However I prefer not to use this as forgetting to remove this before deployment will cause your script to pause, which can be quite embarrassing!
You should use the debug console provided by the browser.
Chrome has it inbuilt, press CTRL + SHIFT + j. In Firefox, install Firebug plugin.
In your code, add alert() to show flow and get values of variables.
Also, use console.log() which will only output to the debug console.
Depending on your browser choice there are debugging options - I tend to use Firefox, so Firebug in my case. There is a question that list options for other browsers - What is console.log and how do I use it?
Unless the project you're working on has already adopted a mechanism for debugging, console.log() tends to be a simple and useful option when tracking down a problem.
Whilst debugging you could take the approach to log out a line when entering a function, like so:
var myFunc = function(el) {
console.log('Inside myFunc');
// Existing code
};
This will enable you to see which functions have been called and give you a rough idea of the order of execution.
You can also use console.log() to show the contents of variables - console.log(el);
Be mindful to remove/disable console.log() calls once you're done as it will likely cause some issues in production.
To answer your question within question,
how can a person making his first javascript function suppose to find that out ???
Well, when something is wrong in JavaScript, for example, you made a syntax error - the script will stop working from there. However, this won't stop HTML from rendering on, so it might look as if everything is correct (especially if your JS is not changing the look of the page) but all the functionality of JS will be dead.
That's why we use the debug tools (listed in the other answers here) to see what's wrong, and in cases like this, it's very easy to notice which function has errors and is causing the whole script to break. This would probably have save a few minutes to your seniors as well.
The best approach would be to test frequently so that whenever you run into errors, you can fix them right away.
I'd like to write a test case (using Selenium, but not the point of this question) to validate that my web application has no script errors\warnings or unhanded exceptions at certain points in time (like after initializing a major library).
This information can easily be seen in the debug consoles of most browsers. Is it possible to execute a javascript statement to get this information programatically?
It's okay if it's different for each browser, I can deal with that.
not so far read about your issue (as far as I understood your problem) here
The idea be the following:
I found, however, that I was often getting JavaScript errors when the page first loaded (because I was working on the JS and was introducing errors), so I was looking for a quick way to add an assert to my test to check whether any JS errors occurred. After some Googling I came to the conclusion that there is nothing built into Selenium to support this, but there are a number of hacks that can be used to accomplish it. I'm going to describe one of them here. Let me state again, for the record, that this is pretty hacky. I'd love to hear from others who may have better solutions.
I simply add a script to my page that will catch any JS errors by intercepting the window.onerror event:
<script type="text/javascript">
window.onerror=function(msg){
$("body").attr("JSError",msg);
}
</script>
This will cause an attribute called JSError with a value corresponding to the JavaScript error message to be added to the body tag of my document if a JavaScript error occurs. Note that I'm using jQuery to do this, so this specific example won't work if jQuery fails to load. Then, in my Selenium test, I just use the command assertElementNotPresent with a target of //body[#JSError]. Now, if any JavaScript errors occur on the page my test will fail and I'll know I have to address them first. If, for some strange reason, I want to check for a particular JavaScript error, I could use the assertElementPresent command with a target of //body[#JSError='the error message'].
Hope this fresh idea helps you :)
try {
//code
} catch(exception) {
//send ajax request: exception.message, exception.stack, etc.
}
More info - MDN Documentation