Is there any way I can get this piece of code to work inside Greasemonkey/Scriptish, or would I have to inject it into the webpage itself?
body = document.getElementsByTagName("body")[0];
fakeConsole = 'window.top._console';
injected = document.getElementById("sandbox") ? true : false;
sandboxframe = injected ? document.getElementsById("sandbox") : document.createElement('iframe');
sandbox = null;
if (!injected) {
body.appendChild(sandboxframe);
sandboxframe.setAttribute('id', 'sandbox');
sandboxframe.setAttribute('style', "display:none")
}
var p = sandboxframe.contentWindow.eval('1 + 1');
console.log(p);
This code does work when using source:
<script type="text/javascript" src="test.js"></script>
But not when using in a Greasemonkey script, I have observed there's some kind of security barrier I'm not quite familiar with and attempted to use unsafeWindow to bypass XPCNativeWrapper.
Please shed some light on this.
Several things:
The code has an error; getElementsById is not a function.
That code otherwise does work in Greasemonkey 1.0 or later, if the #grant none directive applies. More on this below.
For Scriptish, all other browsers, and Greasemonkey scenarios where #grant none is not possible; you will have to "inject" the code. More on this below.
As Jeremy J Starcher says, eval() should be avoided as much as possible. eval() makes performance, maintenance, debugging, and security much harder.
For Greasemonkey 1.0, and later:
In some scenarios, Greasemonkey no longer uses the XPCNativeWrapper. See the doc for the #grant directive.
So this means that (1) If your script uses no GM_ functions and (2) the script specifies #grant none, then your code will run as-is (excepting the getElementsById typo).
Note that no other scripting engine does this. (For darn good reasons. Greasemonkey's new behavior concerning #grant, and the sandbox, is controversial at best.)
If you wish to use GM_ functions, then you must inject the iframe code. See the next section.
For Scriptish, Privileged Greasemonkey, Chrome, etc.:
Scriptish, Sandboxed Greasemonkey, Chrome, etc. all do not handle iframes well from within their respective sandboxes. (See these Q's, for example.)
The only reliable way to run this kind of code from a GM/userscript is to inject it. Like so:
function gmMain () {
body = document.getElementsByTagName("body")[0];
fakeConsole = 'window.top._console';
injected = document.getElementById("sandbox") ? true : false;
sandboxframe = injected ? document.getElementById("sandbox") : document.createElement('iframe');
sandbox = null;
if (!injected) {
body.appendChild(sandboxframe);
sandboxframe.setAttribute('id', 'sandbox');
sandboxframe.setAttribute('style', "display:none")
}
var p = sandboxframe.contentWindow.eval('1 + 1');
console.log(p);
}
addJS_Node (null, null, gmMain);
function addJS_Node (text, s_URL, funcToRun, runOnLoad) {
var D = document;
var scriptNode = D.createElement ('script');
if (runOnLoad) {
scriptNode.addEventListener ("load", runOnLoad, false);
}
scriptNode.type = "text/javascript";
if (text) scriptNode.textContent = text;
if (s_URL) scriptNode.src = s_URL;
if (funcToRun) scriptNode.textContent = '(' + funcToRun.toString() + ')()';
var targ = D.getElementsByTagName ('head')[0] || D.body || D.documentElement;
targ.appendChild (scriptNode);
}
Related
I need to create a hidden iframe in a Firefox add-on using WebExtensions.
This is what I'm trying to do (as suggested here: Load multiple pages in a hidden iframe from a xul-based firefox extension):
var hiddenWindow = Components.classes["#mozilla.org/appshell/appShellService;1"].
getService(Components.interfaces.nsIAppShellService).
hiddenDOMWindow;
I get the error "Components.classes is undefined", and can't seem to work around it.
Help is appreciated. Thank you.
Don't do this. In WEAPI this isn't even possible, you can't run code like this in that scope.
WEAPI
If you are using webextensions use a background page and you don't have to worry about this - https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Anatomy_of_a_WebExtension#Background_scripts
Bootstrap
In bootstrap you can do this, but don't. In bootstrap use the same tech used by WEAPI which is windowless browsers which are supported since Firefox 23 -
var webnav = Services.appShell.createWindowlessBrowser(true);
var docshell = webnav.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDocShell);
var systemPrincipal = Cc["#mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal);
docshell.createAboutBlankContentViewer(systemPrincipal);
var contentWindow = docshell.contentViewer.DOMDocument.defaultView;
// when you are done with it, destroy it
if (webnav.close) { webnav.close() }; // only available in Firefox 46+, and is needed for good measure
webnav = null; // in Firefox <= 45 setting to null will cause it to get GC'ed which will destroy it
https://bugzilla.mozilla.org/show_bug.cgi?id=846906
https://bugzilla.mozilla.org/show_bug.cgi?id=1239822
Here's another example, this is how webextenions use this code above:
let chromeWebNav = Services.appShell.createWindowlessBrowser(true);
this.chromeWebNav = chromeWebNav;
let url;
if (this.page) {
url = this.extension.baseURI.resolve(this.page);
} else {
// TODO: Chrome uses "_generated_background_page.html" for this.
url = this.extension.baseURI.resolve("_blank.html");
}
if (!this.extension.isExtensionURL(url)) {
this.extension.manifestError("Background page must be a file within the extension");
url = this.extension.baseURI.resolve("_blank.html");
}
let system = Services.scriptSecurityManager.getSystemPrincipal();
let chromeShell = chromeWebNav.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell);
chromeShell.createAboutBlankContentViewer(system);
let chromeDoc = chromeWebNav.document;
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let browser = chromeDoc.createElementNS(XUL_NS, "browser");
browser.setAttribute("type", "content");
browser.setAttribute("disableglobalhistory", "true");
browser.setAttribute("webextension-view-type", "background");
chromeDoc.body.appendChild(browser);
let frameLoader = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
let docShell = frameLoader.docShell;
let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
this.webNav = webNav;
webNav.loadURI(url, 0, null, null, null);
let window = webNav.document.defaultView;
this.contentWindow = window;
https://dxr.mozilla.org/mozilla-central/source/toolkit/components/extensions/ext-backgroundPage.js#25-64
I'm trying to convert the JavaScript code
if (window.ifEdit.editIsDirty()) { }
into Typescript. I got as far as the following
var iframe = document.getElementById('ifEdit');
var iWindow = <HTMLIFrameElement>(iframe).contentWindow;
var _editIsDirty = iWindow.editIsDirty();
I get the red squiggles under 'contentWindow' and 'editIsDirty' saying the method/property does not exist on the type. The .ts doesn't compile to a .js file.
I have searched, but did not manage to find a solution.
For the contentWindow part, the problem with your code is that the casting is done wrong, should be:
var iWindow = (<HTMLIFrameElement> iframe).contentWindow;
As for the editIsDirty, it's not a standard property of Window.
If it's something which is added in the environment in which you are running your javascript then you need to declare it like so:
interface IfEdit {
editIsDirty(): boolean;
}
interface Window {
ifEdit: IfEdit;
}
var iframe = document.getElementById("ifEdit");
var iWindow = (<HTMLIFrameElement> iframe).contentWindow;
var _editIsDirty = iWindow.ifEdit.editIsDirty();
Use the code in Playground.
Casting will be through as. this assures .contentWindow is accessible.
const iframe = document.getElementById('embed-player') as HTMLIFrameElement;
if (!iframe) {
// Handle case where iframe not found
return;
}
const contentWindow = iframe.contentWindow;
// Note: You will likely need more null handling for contentWindow's properties
console.log(contentWindow?.document);
Hi I am using selenium webdriver 2.25.0 & faceing the some serious issues,
how to find broken images in a page using Selenium Webdriver
How to find the image is replace by another image having same name (This is also bug) using webdriver.
Thanks in advance for your value-able suggestions.
The accepted answer requires that you use a proxy with an extra call to each image to determine if the images are broken or not.
Fortunately, there is another way you can do this using only javascript (I'm using Ruby, but you can use the same code in any executeScript method across the WebDriver bindings):
images = #driver.find_elements(:tag_name => "img")
broken_images = images.reject do |image|
#driver.execute_script("return arguments[0].complete && typeof arguments[0].naturalWidth != \"undefined\" && arguments[0].naturalWidth > 0", image)
end
# broken_images now has an array of any images on the page with broken links
# and we want to ensure that it doesn't have any items
assert broken_images.empty?
To your other question, I would recommend just taking a screenshot of the page and having a human manually verify the resulting screenshot has the correct images. Computers can do the automation work, but humans do have to check and verify its results from time to time :)
The next lines are not optimized, but they could find broken images:
List<WebElement> imagesList = _driver.findElements(By.tagName("img"));
for (WebElement image : imagesList)
{
HttpResponse response = new DefaultHttpClient().execute(new HttpGet(image.getAttribute("src");));
if (response.getStatusLine().getStatusCode() != 200)
// Do whatever you want with broken images
}
Regarding your second issue, I think I didn't understand it correctly. Could you explain it with more detail?
Based on the other answers, the code that eventually worked for me in an angular / protractor / webdriverjs setting is:
it('should find all images', function () {
var allImgElts = element.all(by.tagName('img'));
browser.executeAsyncScript(function (callback) {
var imgs = document.getElementsByTagName('img'),
loaded = 0;
for (var i = 0; i < imgs.length; i++) {
if (imgs[i].naturalWidth > 0) {
loaded = loaded + 1;
};
};
callback(loaded);
}).then(function (loadedImagesCount) {
expect(loadedImagesCount).toBe(allImgElts.count());
});
});
The webdriver code counts the number of img elements, and the function executed within the browser context counts the number of successfully loaded elements. These numbers should be the same.
Sometimes the ge plugin wont load kml via the fetchKml function. When this happens I have to kill the ge plugin processes and then it works fine.
Is there a way to do this within the browser? A setTimeout will catch when it's not loading and I'd like to reset the plugin so it will work.
I would want something like google.earth.reset();
Thanks
There is no reset function, but there is an undocumented kill method for the GEPlugin object.
// where ge is a reference to the plugin.
ge.kill_();
That said there is probably a reason why the kml does not appear to be loading, as nuxy says, if you post and example of the actual code you are using then it would be easier to offer help.
EDIT:
Also rather than using an undocumented method you could just re-load the database which will reset the plugin. Something like the following set up should work well.
var ge = null;
google.earth.createInstance('map3d', initCallback, failureCallback);
var initCallback = function(object) {
ge = object;
ge.getWindow().setVisibility(true);
}
var failureCallback = function(error) {
alert(error):
}
var reset = function() {
document.getElementById('map3d').innerHTML = '';
google.earth.createInstance('map3d', initCallback, failureCallback);
}
To use simply call the reset method.
reset();
I'm using EAFlashUpload component with ASP.NET 4.0 on my site to allow for multiple file uploads (select multiple files at a time).
The component works great and with no issues with IE (7, 8, 9), but with Firefox or Chrome, it gives an "unknown http error" after a file is uploaded (when it is trying to do postback to the server, I think).
I'm running this under IIS7 on Windows 2008 Server R2, but I don't think the version of IIS or Windows is the significant factor.
This is the client side Javascript to handle the component:
<script type="text/javascript">
var params = { BGcolor: "#ffffff", wmode: "window" };
var attributes = { id: "EAFlashUpload", name: "EAFlashUpload" };
var flashvars = new Object();
var uploadUrl = "Pages/SendAdvanced.aspx";
//if (!document.all) {
// uploadUrl = "../" + uploadUrl;
//}
flashvars["uploader.uploadUrl"] = uploadUrl;
flashvars["viewFile"] = "EAFUpload/TableView.swf";
flashvars["view.removeButton.visible"] = "false";
flashvars["view.clearButton.visible"] = "false";
flashvars["queue.filesCountLimit"] = "10";
flashvars["queue.fileSizeLimit"] = "2147483648"; // 2 gigabytes = 2147483648 bytes
swfobject.embedSWF("EAFUpload/EAFUpload.swf", "EAFlashUpload_holder", "650", "380", "10.0.0", "EAFUpload/expressInstall.swf", flashvars, params, attributes);
function EAFlashUpload_onMovieLoad(errors) { if (errors != "") alert(errors); }
</script>
EDIT:
I discovered after further testing that the Flash component is throwing error #2038, which is a generic catchall error for "cannot connect to the server". Not sure why this is an issue with Chrome and FF, but this warrants additional investigation...
I found the answer, and it is absolutely NOT what I was expecting.
This line of code was the issue:
var uploadUrl = "Pages/SendAdvanced.aspx";
That line is a relative path, set in client side Javascript and used by the Flash component to know where to post the content.
When I changed it to this, it worked:
var uploadUrl = "http://www.mysite.com/Pages/SendAdvanced.aspx";
What I would love for someone to tell me is WHY the first line of code EVER works? Why does it work in Internet Explorer? Is there something special it is able to do to track relative paths, that the other browsers (and the Mac) won't do?
I found this answer when I was experiencing similar issues with the flash uploader (the uploads would fail before even connecting to the server). Using the following example code:
var uploadUrl = location.href;
if (!document.all) {
uploadUrl = "../" + uploadUrl;
}
//alert(uploadUrl);
var flashvars = new Object();
flashvars["uploader.uploadUrl"] = uploadUrl;
...
Turns out the problem only occurred with URLs that had a & and/or # in them (the flash object was receiving these and not knowing what to do with them because they were not encoded). There are more characters than these to worry about, however, in my case, this ended up solving the issue
I simply replaced the ampersands with the encoded version that flash was expecting, and removed the hash pound and everything following it (because it is useless from a server's perspective).
uploadUrl = uploadUrl.replace(/\&/g, '%26');
uploadUrl = uploadUrl.replace(/#.*/g, '');