I am trying to intercept a sites calls to the window and screen objects from a firefox addon, for their width and height attributes and return my own values .
I have been trying to use an observer
const os = Components.classes["#mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
os.addObserver({
observe : function(subject, topic, data) {
if (subject instanceof Ci.nsIDOMWindow && topic == 'content-document-global-created') {
subject.wrappedJSObject.myfunction = function(){
// Do stuff
}
}
}
}, 'content-document-global-created', false);
This allows me to override normal javascript methods but I can't seem to change the screen or window attributes. Would a pagemod be able to modify it with a content script so the site will read the values specified in the addon?
You would do it like this:
subject.wrappedJSObject.screen.__defineGetter__("width", function() { return myWidth });
I'm not sure on the timing as willma pointed out, but this is what the code looks like.
Related
Is it possible to use Text Input Processor on a specific Tab of a Firefox window?
Do all the tabs share the same nsIDOMWindow object? If so, then is there any other solution?
Rephrasing my problem: I want to type text to a specific tab, and if there is more than one, then it might not be the current.
Below is an example code to write text on any Firefox window, but only for current tab:
function myTestFunc() {
// var windows = Services.wm.getEnumerator("navigation:browser");
var windows = require("sdk/windows");
for (let browserWindow of windows.browserWindows) {
//console.log(window.title);
var chromeWindow = viewFor(browserWindow);
console.log(chromeWindow.document.location.href);
var idomWindow = chromeWindow.QueryInterface(Ci.nsIDOMWindow);
var TIP = Cc["#mozilla.org/text-input-processor;1"].
createInstance(Ci.nsITextInputProcessor);
if(!TIP.beginInputTransaction(idomWindow, onNotifyImpl)) {
console.log("Error TIP can't start");
continue;
}
TIP.setPendingCompositionString("foo-bar-buzz");
if (!TIP.flushPendingComposition()) {
console.log("Failed to flush");
continue; // Composition is not started
}
}
}
nsIDOMWindow (+ various related interfaces like the docshell) is the XPCOM representation of regular window objects in the w3c specs. And since window objects can be nested (e.g. via iframes) so can be nsIDOMWindows. When you're accessing browser windows you're generally accessing the outer windows representing the browser chrome, not the content.
In principle you can access a tab's content window directly from its <browser> XUL element, but to be forward-compatible with e10s you should use framescripts instead.
I am writing an iframe based facebook app. Now I want to use the same html page to render the normal website as well as the canvas page within facebook. I want to know if I can determine whether the page has been loaded inside the iframe or directly in the browser?
Browsers can block access to window.top due to same origin policy. IE bugs also take place. Here's the working code:
function inIframe () {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
}
top and self are both window objects (along with parent), so you're seeing if your window is the top window.
When in an iframe on the same origin as the parent, the window.frameElement method returns the element (e.g. iframe or object) in which the window is embedded. Otherwise, if browsing in a top-level context, or if the parent and the child frame have different origins, it will evaluate to null.
window.frameElement
? 'embedded in iframe or object'
: 'not embedded or cross-origin'
This is an HTML Standard with basic support in all modern browsers.
if ( window !== window.parent )
{
// The page is in an iframe
}
else
{
// The page is not in an iframe
}
I'm not sure how this example works for older Web browsers but I use this for IE, Firefox and Chrome without an issue:
var iFrameDetection = (window === window.parent) ? false : true;
RoBorg is correct, but I wanted to add a side note.
In IE7/IE8 when Microsoft added Tabs to their browser they broke one thing that will cause havoc with your JS if you are not careful.
Imagine this page layout:
MainPage.html
IframedPage1.html (named "foo")
IframedPage2.html (named "bar")
IframedPage3.html (named "baz")
Now in frame "baz" you click a link (no target, loads in the "baz" frame) it works fine.
If the page that gets loaded, lets call it special.html, uses JS to check if "it" has a parent frame named "bar" it will return true (expected).
Now lets say that the special.html page when it loads, checks the parent frame (for existence and its name, and if it is "bar" it reloads itself in the bar frame. e.g.
if(window.parent && window.parent.name == 'bar'){
window.parent.location = self.location;
}
So far so good. Now comes the bug.
Lets say instead of clicking on the original link like normal, and loading the special.html page in the "baz" frame, you middle-clicked it or chose to open it in a new Tab.
When that new tab loads (with no parent frames at all!) IE will enter an endless loop of page loading! because IE "copies over" the frame structure in JavaScript such that the new tab DOES have a parent, and that parent HAS the name "bar".
The good news, is that checking:
if(self == top){
//this returns true!
}
in that new tab does return true, and thus you can test for this odd condition.
The accepted answer didn't work for me inside the content script of a Firefox 6.0 Extension (Addon-SDK 1.0): Firefox executes the content script in each: the top-level window and in all iframes.
Inside the content script I get the following results:
(window !== window.top) : false
(window.self !== window.top) : true
The strange thing about this output is that it's always the same regardless whether the code is run inside an iframe or the top-level window.
On the other hand Google Chrome seems to execute my content script only once within the top-level window, so the above wouldn't work at all.
What finally worked for me in a content script in both browsers is this:
console.log(window.frames.length + ':' + parent.frames.length);
Without iframes this prints 0:0, in a top-level window containing one frame it prints 1:1, and in the only iframe of a document it prints 0:1.
This allows my extension to determine in both browsers if there are any iframes present, and additionally in Firefox if it is run inside one of the iframes.
I'm using this:
var isIframe = (self.frameElement && (self.frameElement+"").indexOf("HTMLIFrameElement") > -1);
Use this javascript function as an example on how to accomplish this.
function isNoIframeOrIframeInMyHost() {
// Validation: it must be loaded as the top page, or if it is loaded in an iframe
// then it must be embedded in my own domain.
// Info: IF top.location.href is not accessible THEN it is embedded in an iframe
// and the domains are different.
var myresult = true;
try {
var tophref = top.location.href;
var tophostname = top.location.hostname.toString();
var myhref = location.href;
if (tophref === myhref) {
myresult = true;
} else if (tophostname !== "www.yourdomain.com") {
myresult = false;
}
} catch (error) {
// error is a permission error that top.location.href is not accessible
// (which means parent domain <> iframe domain)!
myresult = false;
}
return myresult;
}
Best-for-now Legacy Browser Frame Breaking Script
The other solutions did not worked for me. This one works on all browsers:
One way to defend against clickjacking is to include a "frame-breaker" script in each page that should not be framed. The following methodology will prevent a webpage from being framed even in legacy browsers, that do not support the X-Frame-Options-Header.
In the document HEAD element, add the following:
<style id="antiClickjack">body{display:none !important;}</style>
First apply an ID to the style element itself:
<script type="text/javascript">
if (self === top) {
var antiClickjack = document.getElementById("antiClickjack");
antiClickjack.parentNode.removeChild(antiClickjack);
} else {
top.location = self.location;
}
</script>
This way, everything can be in the document HEAD and you only need one method/taglib in your API.
Reference: https://www.codemagi.com/blog/post/194
I actually used to check window.parent and it worked for me, but lately window is a cyclic object and always has a parent key, iframe or no iframe.
As the comments suggest hard comparing with window.parent works. Not sure if this will work if iframe is exactly the same webpage as parent.
window === window.parent;
Since you are asking in the context of a facebook app, you might want to consider detecting this at the server when the initial request is made. Facebook will pass along a bunch of querystring data including the fb_sig_user key if it is called from an iframe.
Since you probably need to check and use this data anyway in your app, use it to determine the the appropriate context to render.
function amiLoadedInIFrame() {
try {
// Introduce a new propery in window.top
window.top.dummyAttribute = true;
// If window.dummyAttribute is there.. then window and window.top are same intances
return !window.dummyAttribute;
} catch(e) {
// Exception will be raised when the top is in different domain
return true;
}
}
Following on what #magnoz was saying, here is a code implementation of his answer.
constructor() {
let windowLen = window.frames.length;
let parentLen = parent.frames.length;
if (windowLen == 0 && parentLen >= 1) {
this.isInIframe = true
console.log('Is in Iframe!')
} else {
console.log('Is in main window!')
}
}
It's an ancient piece of code that I've used a few times:
if (parent.location.href == self.location.href) {
window.location.href = 'https://www.facebook.com/pagename?v=app_1357902468';
}
If you want to know if the user is accessing your app from facebook page tab or canvas check for the Signed Request. If you don't get it, probably the user is not accessing from facebook.
To make sure confirm the signed_request fields structure and fields content.
With the php-sdk you can get the Signed Request like this:
$signed_request = $facebook->getSignedRequest();
You can read more about Signed Request here:
https://developers.facebook.com/docs/reference/php/facebook-getSignedRequest/
and here:
https://developers.facebook.com/docs/reference/login/signed-request/
This ended being the simplest solution for me.
<p id="demofsdfsdfs"></p>
<script>
if(window.self !== window.top) {
//run this code if in an iframe
document.getElementById("demofsdfsdfs").innerHTML = "in frame";
}else{
//run code if not in an iframe
document.getElementById("demofsdfsdfs").innerHTML = "no frame";
}
</script>
if (window.frames.length != parent.frames.length) { page loaded in iframe }
But only if number of iframes differs in your page and page who are loading you in iframe. Make no iframe in your page to have 100% guarantee of result of this code
Write this javascript in each page
if (self == top)
{ window.location = "Home.aspx"; }
Then it will automatically redirects to home page.
I have a web application that allows users to enter search criteria and the results get appended to a portlet container right below the input fields for the search criteria (mentioned above).
Environment - JSP, JAVA, and TAG libraries
The search page essentially consists of a few JSP pages which reference a myriad tag libraries.
After the search results are returned (AJAX) - the portlet iframe should re-size based on the new height. A JavaScript method (resident to one of the tag libraries) called setContainerHeight is what does this re-sizing.
The problem -
Internet Explorer does not call my JS method which does this re-size!
I suspect that the following post could help with this problem.
Javascript IE Event
Chrome, Firefox, Opera....all do!
Since I cannot explicitly tell the page to refresh (I will loose my search criteria parameters), nor can I explicitly check what type of browser is making the request (my JavaScript is not getting called); how can I explicitly tell the page to call my resize method after the callback?
In case anyone was curious - this is my re-size method:
function setContainerHeight() {
//reset the height to shrink the scroll height for the usecase where the portlet's contents got smaller.
getPortlet().style.height = '${frameHeight}px';
getPortlet().height = '${frameHeight}px';
try {
height = getFrameHeight(getPortlet());
} catch (e) {
//fallback for crossdomain permission problems.
height = '${frameHeight}';
}
//set the portlet & portlet container to be the same height - not using 100% for the portlet to avoid the inner scrollbar
try {
getPortletContainer().style.height = height + 'px';
} catch ( ex ) {
// do nothing, we can't get to the container
}
getPortlet().style.height = (height + getHorScrollBarHeight()) + 'px';
getPortlet().height = (height + getHorScrollBarHeight()) + 'px';
}
And this is the section of the code that calls this method -
/* resizes the portlet container to fit the size of the porlet. */
function resizePortletContainer() {
if (hasContainerHeightChanged()) {
saveScrollPosition();
setContainerHeight();
restoreScrollPosition();
}
//width handling needs some work
//if (hasContainerWidthChanged()) {
//setContainerWidth();
//}
}
//registering event handlers...
var frame = getPortlet();
var prevPortletLoadEvent = frame.onload ? frame.onload : function () {};
frame.onload = function () {prevPortletLoadEvent(); resizePortletContainer(); };
var prevPortletResizeEvent = frame.onresize ? frame.onresize : function () {};
var onresize = function () {prevPortletResizeEvent(); resizePortletContainer(); };
A bit more information -
After placing alert statements after the register event handler code above; I noticed that both IE and Firefox called this portion of code only ONCE (my alert box was triggered only once in either case when the initial search screen was displayed to the browser.) Thus, that leads me to believe that for some reason, only Firefox likes the above code that I am using to register my events; IE perhaps is looking for a different method of registering this event?
I suspect that the following post could be useful in my search for the answer to this problem --
Javascript IE Event
I found my answer in another post, but essentially the problem was that Internet Explorer registers its iframe events differently than all other browsers.
Also a good post for you to look at - iframe behaviour of onload vs addEventListener('load')
The code that I used to fix the problem is listed below. I hope this helps someone else that gets into a similar problem I had.
I had to do a simple check on what browser was making the request and the 2 conditional blocks that you see below do the same thing, except the syntax is different for IE. Good 'ol IE. LoL.
//Microsoft Internet Explorer Browser - iFrame event register
if (navigator.appName.indexOf("Microsoft") != -1) {
var frame = getPortlet();
var prevPortletLoadEvent = frame.onload ? frame.onload : function () {};
var refresh = function() { prevPortletLoadEvent(); resizePortletContainer(); };
if(frame.attachEvent) frame.attachEvent('onload', refresh);
else frame.addEventListener('load', refresh, false);
}
//All other major browsers - iFrame event register
else {
var frame = getPortlet();
var prevPortletLoadEvent = frame.onload ? frame.onload : function () {};
frame.onload = function () {prevPortletLoadEvent(); resizePortletContainer(); };
var prevPortletResizeEvent = frame.onresize ? frame.onresize : function () {};
var onresize = function () {prevPortletResizeEvent(); resizePortletContainer(); };
}
I'm developing a firefox extension which requires me to intercept page loads by filtering out some HTTPRequests.
I did that using the instructions given here. Please note that my question draws from the content of this link.
I used the method given under the section of HTTPObservers. And it worked, I am indeed able to extract the respective urls of the Requests being sent out.
However, another thing which I really require is to get the target DOM Window where the contents pertaining to the HTTPRequest were about to be loaded. Is it possible using HTTPObservers?
In the link above, another way has been described using WebProgressListeners.
I tried that out as well. The onLocationChange() method only returns location changes in the url bar. Is it somehow possible to get the HTTPRequest urls using any of these progress listeners? Because if so, then if I understand correctly, aWebProgress.DOMWindow would give me the window I require.
Note: I am using gwt for the extension and the JSNI for the above mentioned part.
You can usually do that by using nsILoadContext interface (sadly barely documented) attached to the request or its load group. Here is how you would do that:
function getWindowForRequest(request)
{
if (request instanceof Components.interfaces.nsIRequest)
{
try
{
if (request.notificationCallbacks)
{
return request.notificationCallbacks
.getInterface(Components.interfaces.nsILoadContext)
.associatedWindow;
}
} catch(e) {}
try
{
if (request.loadGroup && request.loadGroup.notificationCallbacks)
{
return request.loadGroup.notificationCallbacks
.getInterface(Components.interfaces.nsILoadContext)
.associatedWindow;
}
} catch(e) {}
}
return null;
}
Note that this function is expected to return null occasionally - not every HTTP request is associated with a window.
I can detect when the content of an iframe has loaded using the load event. Unfortunately, for my purposes, there are two problems with this:
If there is an error loading the page (404/500, etc), the load event is never fired.
If some images or other dependencies failed to load, the load event is fired as usual.
Is there some way I can reliably determine if either of the above errors occurred?
I'm writing a semi-web semi-desktop application based on Mozilla/XULRunner, so solutions that only work in Mozilla are welcome.
If you have control over the iframe page (and the pages are on the same domain name), a strategy could be as follows:
In the parent document, initialize a variable var iFrameLoaded = false;
When the iframe document is loaded, set this variable in the parent to true calling from the iframe document a parent's function (setIFrameLoaded(); for example).
check the iFrameLoaded flag using the timer object (set the timer to your preferred timeout limit) - if the flag is still false you can tell that the iframe was not regularly loaded.
I hope this helps.
This is a very late answer, but I will leave it to someone who needs it.
Task: load iframe cross-origin content, emit onLoaded on success and onError on load error.
This is the most cross browsers origin independent solution I could develop. But first of all I will briefly tell about other approaches I had and why they are bad.
1. iframe That was a little shock for me, that iframe only has onload event and it is called on load and on error, no way to know it is error or not.
2. performance.getEntriesByType('resource'). This method returns loaded resources. Sounds like what we need. But what a shame, firefox always adds Resource in resources array no matter it is loaded or failed. No way to know by Resource instance was it success. As usual. By the way, this method does not work in ios<11.
3. script I tried to load html using <script> tag. Emits onload and onerror correctly, sadly, only in Chrome.
And when I was ready to give up, my elder collegue told me about html4 tag <object>. It is like <iframe> tag except it has fallbacks when content is not loaded. That sounds like what we are need! Sadly it is not as easy as it sounds.
CODE SECTION
var obj = document.createElement('object');
// we need to specify a callback (i will mention why later)
obj.innerHTML = '<div style="height:5px"><div/>'; // fallback
obj.style.display = 'block'; // so height=5px will work
obj.style.visibility = 'hidden'; // to hide before loaded
obj.data = src;
After this we can set some attributes to <object> like we'd wanted to do with iframe. The only difference, we should use <params>, not attributes, but their names and values are identical.
for (var prop in params) {
if (params.hasOwnProperty(prop)) {
var param = document.createElement('param');
param.name = prop;
param.value = params[prop];
obj.appendChild(param);
}
}
Now, the hard part. Like many same-like elements, <object> doesn't have specs for callbacks, so each browser behaves differently.
Chrome. On error and on load emits load event.
Firefox. Emits load and error correctly.
Safari. Emits nothing....
Seems like no different from iframe, getEntriesByType, script....
But, we have native browser fallback! So, because we set fallback (innerHtml) directly, we can tell if <object> is loaded or not
function isReallyLoaded(obj) {
return obj.offsetHeight !== 5; // fallback height
}
/**
* Chrome calls always, Firefox on load
*/
obj.onload = function() {
isReallyLoaded(obj) ? onLoaded() : onError();
};
/**
* Firefox on error
*/
obj.onerror = function() {
onError();
};
But what to do with Safari? Good old setTimeout.
var interval = function() {
if (isLoaded) { // some flag
return;
}
if (hasResult(obj)) {
if (isReallyLoaded(obj)) {
onLoaded();
} else {
onError();
}
}
setTimeout(interval, 100);
};
function hasResult(obj) {
return obj.offsetHeight > 0;
}
Yeah.... not so fast. The thing is, <object> when fails has unmentioned in specs behaviour:
Trying to load (size=0)
Fails (size = any) really
Fallback (size = as in innnerHtml)
So, code needs a little enhancement
var interval = function() {
if (isLoaded) { // some flag
return;
}
if (hasResult(obj)) {
if (isReallyLoaded(obj)) {
interval.count++;
// needs less then 400ms to fallback
interval.count > 4 && onLoadedResult(obj, onLoaded);
} else {
onErrorResult(obj, onError);
}
}
setTimeout(interval, 100);
};
interval.count = 0;
setTimeout(interval, 100);
Well, and to start loading
document.body.appendChild(obj);
That is all. I tried to explain code in every detail, so it may look not so foolish.
P.S. WebDev sucks
I had this problem recently and had to resort to setting up a Javascript Polling action on the Parent Page (that contains the IFRAME tag). This JavaScript function checks the IFRAME's contents for explicit elements that should only exist in a GOOD response. This assumes of course that you don't have to deal with violating the "same origin policy."
Instead of checking for all possible errors which might be generated from the many different network resources.. I simply checked for the one constant positive Element(s) that I know should be in a good response.
After a pre-determined time and/or # of failed attempts to detect the expected Element(s), the JavaScript modifies the IFRAME's SRC attribute (to request from my Servlet) a User Friendly Error Page as opposed to displaying the typical HTTP ERROR message. The JavaScript could also just as easily modify the SRC attribute to make an entirely different request.
function checkForContents(){
var contents=document.getElementById('myiframe').contentWindow.document
if(contents){
alert('found contents of myiframe:' + contents);
if(contents.documentElement){
if(contents.documentElement.innerHTML){
alert("Found contents: " +contents.documentElement.innerHTML);
if(contents.documentElement.innerHTML.indexOf("FIND_ME") > -1){
openMediumWindow("woot.html", "mypopup");
}
}
}
}
}
I think that the pageshow event is fired for error pages. Or if you're doing this from chrome, then your check your progress listener's request to see if it's an HTTP channel in which case you can retrieve the status code.
As for page dependencies, I think you can only do this from chrome by adding a capturing onerror event listener, and even then it will only find errors in elements, not CSS backgrounds or other images.
Doesn't answer your question exactly, but my search for an answer brought me here, so I'm posting just in case anyone else had a similar query to me.
It doesn't quite use a load event, but it can detect whether a website is accessible and callable (if it is, then the iFrame, in theory, should load).
At first, I thought to do an AJAX call like everyone else, except that it didn't work for me initially, as I had used jQuery. It works perfectly if you do a XMLHttpRequest:
var url = http://url_to_test.com/
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status != 200) {
console.log("iframe failed to load");
}
};
xhttp.open("GET", url, true);
xhttp.send();
Edit:
So this method works ok, except that it has a lot of false negatives (picks up a lot of stuff that would display in an iframe) due to cross-origin malarky. The way that I got around this was to do a CURL/Web request on a server, and then check the response headers for a) if the website exists, and b) if the headers had set x-frame-options.
This isn't a problem if you run your own webserver, as you can make your own api call for it.
My implementation in node.js:
app.get('/iframetest',function(req,res){ //Call using /iframetest?url=url - needs to be stripped of http:// or https://
var url = req.query.url;
var request = require('https').request({host: url}, function(response){ //This does an https request - require('http') if you want to do a http request
var headers = response.headers;
if (typeof headers["x-frame-options"] != 'undefined') {
res.send(false); //Headers don't allow iframe
} else {
res.send(true); //Headers don't disallow iframe
}
});
request.on('error',function(e){
res.send(false); //website unavailable
});
request.end();
});
Have a id for the top most (body) element in the page that is being loaded in your iframe.
on the Load handler of your iframe, check to see if getElementById() returns a non null value.
If it is, iframe has loaded successfully. else it has failed.
in that case, put frame.src="about:blank". Make sure to remove the loadhandler before doing that.
If the iframe is loaded on the same origin as the parent page, then you can do this:
iframeEl.addEventListener('load', function() {
// NOTE: contentDocument is null if a connection error occurs or if
// X-Frame-Options is not SAMESITE (which could happen with
// 4xx or 5xx error pages if the corresponding error handlers
// do not specify SAMESITE). If error handlers do not specify
// SAMESITE, then networkErrorOccurred will incorrectly be set
// to true.
const networkErrorOccurred = !iframeEl.contentDocument;
const serverErrorOccurred = (
!networkErrorOccurred &&
!iframeEl.contentDocument.querySelector('#well-known-element')
);
if (networkErrorOccurred || serverErrorOccurred) {
let errorMessage;
if (networkErrorOccurred) {
errorMessage = 'Error: Network error';
} else if (serverErrorOccurred) {
errorMessage = 'Error: Server error';
} else {
// Assert that the above code is correct.
throw new Error('networkErrorOccurred and serverErrorOccurred are both false');
}
alert(errorMessage);
}
});