Content Scripts: Page-worker - <body> element with 0 width - javascript

I want to automatically open a website and reveal details about the rendered DOM. For that purpose I use a page-worker (https://developer.mozilla.org/en-US/Add-ons/SDK/High-Level_APIs/page-worker). When in the content script I execute something like the following:
var body = document.getElementsByTagName("body")[0];
console.log(getComputedStyle(body).width);
console.log(body.getBoundingClientRect().width);
The output is "0" for both of the width values. The same also applies to other elements. Should I conclude that DOM elements are not rendered properly when using a page-worker? Is there a way to gain authentic data from that? Or is there any other proper way to get details about the rendered DOM that behave exactly like the website was opened in a web browser by hand? Because if I open the same website manually and execute the same commands, it reveals (correctly) the width of the window (1440), of course.
edit
I probably should have said that before, but:
It's not that gaining the values does not work at all. If I go deeper in the DOM structure, I get correct values. I only have problems with the root body element, I think. Weirdly enough if I execute the exact same commands on the firefox console, the return values are perfectly correct.

It's very interesting the way you do it.
You don't have to do getElementsByTagName to get the body element, its a property of the document object var body = document.body;
getComputedStyle is a function of the window object so that's why it works without you putting window. in front of it but still just want to let you know.
You dont do a getPropertyValue which is required for getComputedStyle, I just tried it without it and it worked but i never saw it used like that before so maybe that has something to do with it:
So doing this from bootstrap or from scratchpad window: gBrowser.contentWindow.getComputedStyle(gBrowser.contentDocument.body).getPropertyValue('width')
works for me.
From your content-script scope do:
window.getComputedStyle(document.body).getPropertyValue('width')
If it still doesn't work I would thinky maybe myabe maybe, thers a chance you might be executing the code to early.
Let me know how that works.

Related

Can't get access elements with querySelector?

I wish to access elements in a site with javascript and I have been using javascript for quite some time, especially with the querySelector functions. But for some reason, it won't work in this case; whenever I try to access any of the top-level elements, as illustrated below, no elements are found.
Does anyone have an idea about what the problem could be, and maybe even how to fix it?
That's normal,
It's because when you use the chrome developer console you can change the document context and in your screen you are inside the iframe.
You can see it here:

Get full actual html page source including the frameset

For our functional test automation we use QTP with the Webtest Plugin. I have control over the DOM (but not in an easy manner) and can use VBScript and partially Javascript to find a solution.
Whenever we encounter an error during a test I'd like to capture the full HTML page source at that moment. Later, when we are inspecting the error from our reports, we can see what happened and how the DOM looked like at that moment.
Therefore I look for a posibility to capture this source. Normally I did it with
htmlSource = browser("micClass:=Browser").page("micClass:=Page").Object.documentElement.outerHTML
or
htmlSource = browser("micClass:=Browser").page("micClass:=Page").Object.getElementsByTagName("html")(0).innerHTML
Unfortunately this will only capture the full content of the tag the frameset, and the frames, but not the actual content that is located in the frames. (classic frames here, not IFrames)
Now I'd like a way to capture the full DOM source at real time including the content in the framesets. And I'd like them in the right order and place, just like the source appears in the HTML view of the IE Developer Tool.
Does anyone have an idea how I could manage that?
If you really need to have the frames' source inline in the page's HTML the only way I know of achieving this is build the HTML recursively for each element. This may be a lot of work since for every element you'll have to remove the innerHTML from the outerHTML before going to the child elements (and insert them correctly).
If you can live with the frames' HTML being outside you can use Page.ChildObjects() with a description micclass=Frame and then use the same mechanism you used for the page (frames(i).Object.documentElement.outerHTML).
Note that you'll get all the frames under the page so there's no need to get the child objects of the Frame test objects even if the frames are nested in the DOM.

How to use page-mod to modify element loaded by JavaScript

I'm creating firefox addon to add onclick event to the specific button. ("input" element)
The button is placed in http://example.com/welcome#_pg=compose
but when I open the page, following error occures:
TypeError: document.querySelector("#send_top") is null
#send_top is id of the button which I want to modify. So, the button is not found.
This error occurs because http://example.com/welcome and http://example.com/welcome#_pg=compose is completely different pages.
In this case, the addon seems loading http://example.com/welcome but there is no button whose '#send_top' ID.
When #_pg=compose anchor is added, the button is loaded by JavaScript.
How can I load http://example.com/welcome#_pg=compose to modify the button?
Three thoughts to help you debug this:
to correctly match the url you should consider using a regular expression instead of the page-match syntax - this might allow you to react to the anchors in a more predictable way
I've found that when using content scripts with pages that are heavily modified by JS, you can run into timing issues. A hacky workaround might be to look for the element you want and, if it isn' there, do a setTimeout for a 100 milliseconds or so and then re-check. Ugly, yes, but it worked for some example code I used with the new twitter UI, for example.
You can use the unsafeWindow variable in your content script to directly access the page's window object - this object will contain any changes JS has made to the page and is not proxied. You should use unsafeWindow with great caution however as its use represent a possible security problem. In particular, you should never trust any data coming from unsafeWindow, ever.

<canvas> in greasemonkey

I've created a <canvas>, in the variable canvas, but I can't seem to draw to it. I can see the blank canvas on the page, but it's blank.
alert(ctx);
ctx.fillStyle = "rgb(50,50,50)";
ctx.fillRect(10,10, 55,50);
alert("done");
The first alert says [object CanvasRenderingContext2D], and I do see the "done" alert, but the canvas is still blank. There are no relevant errors in the error console.
edit: Just to make sure, I pasted the script into a stand alone html page, and it worked as expected.
Canvas works fine for me in Greasemonkey. Post your Greasemonkey script; and what browser are you using?
I've figured out what the problem is, and I'm not sure if it's really a problem in firefox/greasemonkey.
First of all, the script is written for a private site, you'd need an invite to to see the page that it's on, so I posted what I thought was the relevant part of the script, but turns out it wasn't.
The problem was that after creating the canvas, I modified (+=) the .innerHTML of the content div (which I know is bad practice, but it works for greasemonkey that doesn't need to work in every browser). I imagine that this would completely destroy the previous tree, and then recreate it with the new content string given, which would mean that the canvas I had a reference to was no longer the one being displayed on the page, it was one that had been recreated, so my reference was meaningless, and hence when I drew to it, I got no error, but saw nothing. I imagine that if I used that reference to add it back to the page, I should have seen what I'd drawn.
The problem was fixed by not appending to the .innerHTML, I imagine that if I use the proper DOM methods to create the div that I want to add, and then append it to the tree, I should not have this problem (have not yet tested this, though).

How to dynamically add a Javascript function (and invoke)

Based on a click event on the page, via ajax I fetch a block of html and script, I am able to take the script element and append it to the head element, however WebKit based browsers are not treating it as script (ie. I cannot invoke a function declared in the appended script).
Using the Chrome Developer Tools I can see that my script node is indeed there, but it shows up differently then a script block that is not added dynamically, a non-dynamic script has a text child element and I cannot figure out a way to duplicate this for the dynamic script.
Any ideas or better ways to be doing this? The driving force is there is potentially a lot of html and script that would never be needed unless a user clicks on a particular tab, in which case the relevant content (and script) would be loaded. Thanks!
You could try using jQuery... it provides a method called .getScript that will load the JavaScript dynamically in the proper way. And it works fine in all well known browsers.
How about calling eval() on the content you receive from the server? Of course, you have to cut off the <script> and </script> parts.
If you're using a library like jQuery just use the built-in methods for doing this.
Otherwise you'd need to append it to the document rather than the head like this:
document.write("<scr" + "ipt type=\"text/javascript\" src=\"http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js\"></scr" + "ipt>");
In all honesty, I have no idea why the script tag is cut like that, but a lot of examples do that so there's probably a good reason.
You'll also need to account for the fact that loading the script might take quite a while, so after you've appended this to the body you should set up a timer that checks if the script is loaded. This can be achieved with a simple typeof check on any global variable the script exports.
Or you could just do an eval() on the actual javascript body, but there might be some caveats.
Generally speaking though, I'd leave this kind of thing up to the browser cache and just load the javascript on the page that your tabs are on. Just try not to use any onload events, but rather call whatever initializers you need when the tab is displayed.

Categories

Resources