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
Related
I'm relatively new to using ASP webforms and Telerik, but I'm looking for a way that allows me to type special characters (é, ù, à, ...) in a RadComboBox.
Lets say I have a name in my ObjectDataSource called "René Somebody". I need to be able to find him by searching for "Rene" and "René", but so far no luck.
In the application they managed to do this on a RadGrid with filters, but this same solution doesn't work for the RadComboBox as far as I know.
The solution they used in the RadGrid: http://www.telerik.com/forums/accent-insensitive-filtering-filtering-on-a-different-column#YS1QT8P1U0-cRPFNfjvDzA
I have no access to the backend components but the demo you linked contains frontend code and it looks like you can hack in there. It looks like this control may be both client-server and client-side only. For client-side only hacks looks kind of complicated and invloves non-public API (_onInputChange) but for client-server case (which is probably your case) the doc on client side of RadComboBox Object mentions requestItems method so hacking it is probably reasonably future safe:
var hackRadComboBoxFilter = function (combobox, filterProcessingFunction) {
var oldRequestItems = combobox.requestItems;
combobox.requestItems = function() {
var args = Array.prototype.slice.call(arguments);
// requestItems has several arguments but the text seems to be the
// first one, so let's modify it and call the original method
var origFilter = args[0];
args[0] = filterProcessingFunction(origFilter);
oldRequestItems.apply(this, args);
}
};
Unfortunately I don't know a built-in way to deal with accents in JS but you can hack something simple here as well:
var accents = 'ÀÁÂÃÄÅàáâãäåÒÓÔÕÕÖØòóôõöøÈÉÊËèéêëðÇçÐÌÍÎÏìíîïÙÚÛÜùúûüÑñŠšŸÿýŽž';
var mappedAccents = "AAAAAAaaaaaaOOOOOOOooooooEEEEeeeeeCcDIIIIiiiiUUUUuuuuNnSsYyyZz";
var removeAccents = function (origStr) {
var components = [];
var len = origStr.length;
var afterLastAccent = 0;
for (var i = 0; i < len; i++) {
var mapPos = accents.indexOf(origStr[i]);
if (mapPos != -1) {
components.push(origStr.substr(afterLastAccent, i - afterLastAccent) + mappedAccents[mapPos]);
afterLastAccent = i + 1;
}
}
if (afterLastAccent < len)
components.push(origStr.substr(afterLastAccent, len - afterLastAccent));
return components.join('');
};
So now you can combine it in something like this:
// In real app you probably want something like this
// var targetComboBox = $find("<%= RadComboBox1.ClientID %>");
// but for test let's just hack first combobox on the page
var targetComboBox = Telerik.Web.UI.RadComboBox.ComboBoxes[0];
hackRadComboBoxFilter(targetComboBox, removeAccents);
or if you want to modify all the comboboxes on the page, you can change prototype using the same trick:
hackRadComboBoxFilter(Telerik.Web.UI.RadComboBox.prototype, removeAccents)
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.
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);
}
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, '');