Safe way to interact with page's DOM from Overlay JS - javascript

I have a Firefox extension that detects whenever a page loads in the browser and returns its window and document. I want to attach some events (that launch functions in my addon's overlay) to elements in the page, but I don't know how to do this in a way that's safe.
Here's a code sample:
var myExt = {
onInit: function(){
var appcontent = document.getElementById("appcontent");
if(appcontent){
appcontent.addEventListener("DOMContentLoaded", this.onPageLoad, true);
}
},
onPageLoad: function(e){
var doc = e.originalTarget;
var win = doc.defaultView;
doc.getElementById("search").focus = function(){
/* ... 'Some privelliged code here' - unsafe? ... */
};
}
};
So can anyone tell me what's the safe way to add these events/interact with the page's DOM?
Thanks in advance!

I think that you want to listen to the focus event, not replace the focus() function:
doc.getElementById("search").addEventListener("focus", function(event)
{
if (!event.isTrusted)
return;
...
}, false);
Usually, there is fairly little that can go wrong here because you are not accessing the page directly - there is already a security layer (which is also why replacing the focus() method will have no effect). You can also make sure that you only act on "real" events and not events that have been generated by the webpage, you check event.isTrusted for that like in the example code. But as long as you don't unwrap objects or run code that you got from the website, you should be safe.

Related

How to get full loaded HTML source from web site in a Chrome Extension

I need the same source i can find in the Elements window of DevTool console in my extension. I tried using the content script
var text = document.documentElement.innerHTML;
injected after catched the "complete" status from chrome.tabs.onUpdated.addListener, but i recived only the html code without the content dynamically created.
In particular i want my extension to find all "div" added dynamically.
Any help will be appreciated!
The complete event fires once the initial page content has been loaded. It has no relation to dynamically generated content, otherwise it would have to wait indefinitely, since more content may always be added later.
If you are interested in a specific element, you can use setTimeout to periodically poll for the element. Like so:
function getElement() {
return new Promise(function(res, rej) {
var interval = setInterval(function() {
var elm = document.getElementById('the-element-you-want');
if(elm){
clearInterval(interval);
res(elm);
}
}, 10);
});
}
Another option would be to use a MutationObserver to detect when the desired element(s) have been created.

Hide keystrokes from window-wide listeners

I'm making an extension for Chrome to be used on Youtube. The extension adds an overlay on top of the video with a text input. However, typing into the extension triggers Youtube's various keystroke listeners (e.g. space -> pause). event.stopPropagation() does nothing, neither does return false at the end of the Angular event callback.
My current (successfully prototyped but not yet implemented) solution is to wrap the input in an iframe, which will pass messages back to the parent window:
iframe.contentWindow.document.querySelector("#wrapped-input").addEventListener("input", function(){
result.innerHTML = this.value;
});
I feel that this solution is a bit of a hack, and I'm hoping to find something more elegant. As an aside, I am using AngularJS for the app, so if there are any Angular-specific workarounds, I'd love to know those too. Thoughts?
EDIT
Current solution:
<iframe id="wrapper-frame"></iframe>
...
link: {
var input = '<input id="inner-input" />';
var wrap = $window.document.querySelector('#wrapper-iframe').contentWindow.document;
$scope.commentInput = wrap.querySelector('#inner-input');
wrap.open();
wrap.write(input);
wrap.close();
$scope.commentInput.addEventListener('input', function(){
var val = this.value;
$scope.$applyAsync(function(){
$scope.inputContent = val;
});
});
}
It works, but still -- iframe. Bleh, hack. I will leave the question open in case if someone has a better idea.

Is there any way to get "global" events when working with nested iframes?

My problem is I need to capture a keypress but at any given time the user can be inside of an iframe inside of an iframe inside of an iframe.
At first, I thought I could just put the listener on document but that doesn't work if the user is inside of one of those iframes.
Next I thought of attaching the handler to window but ie doesn't support attaching event handlers to window. I'm not even sure if that would work though or it would be the same problem as with document.
Next, I thought I could just go through all the iframes and add individual handlers there but eventually realized that wouldn't work because the iframe doesn't have any html in the dom so there is no way to access iframes nested in it.
One other possible solution is to go to the js of all the iframes and add this code manually but that is way too extreme.
Any other ideas?
I keep answering my own questions but oh well. If you come across this problem I figured out the answer. This uses jquery but you could port it to straight js if you had to.
var myFunction = function () {
alert("hello world");
};
var $myWindow = $(myWindow /*dom object*/);
$myWindow.bind("mouseover", myFunction);
var getNested_iframes = function (document_element) {
$.each(document_element.find("iframe"), function () {
var iframeDocument = $(this).get(0).contentWindow.document; // may need to change this depending on browser
iframeDocument.onkeyup = myFunction;
getNested_iframes($(iframeDocument));
});
}
getNested_iframes($myWindow);

Event before 'load event' for Firefox extension?

I'm writing a Firefox extension,
This is my XUL (no problem there)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE overlay SYSTEM "chrome://locale/myDtd.dtd">
<page id="overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<script type="application/x-javascript" src="chrome://addon/content/test.js" />
</page>
And here is the problematic part of the Javascript test.js
window.addEventListener("load",
function(event) {
var appcontent = window.document.getElementById("appcontent");
appcontent.addEventListener("load",onEventLoad,true);
}, true);
The second 'load' listener on appcontent is too slow for my needs.
The 'load' event is triggered when the DOM is done loading.
My question: Does anyone have an idea of how to run code as soon as a document starts loading (before the load event of the DOM) ? (wish a onBeforeLoad or onRequestStart event existed)
In Chrome extensions, we can use "run_at": "document_start" in 'manifest.json',
and in Safari extensions, we can use 'Starting script' in extension builder
but in Firefox ... I don't know how to do the same trick.
I need this to start looking at elements in the DOM as soon as they arrive (but that's another story).
I appreciate any help.
There's a good explanation here:
http://blog.webmynd.com/2011/04/04/equivalent-to-beforeload-event-for-firefox-extensions/
The suggested solution relies on Firefox Observers and http-on-modify-request:
Components.classes["#mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService)
.addObserver({
observe: function(aSubject, aTopic, aData) {
if ("http-on-modify-request" == aTopic) {
var url = aSubject
.QueryInterface(Components.interfaces.nsIHttpChannel)
.originalURI.spec;
if (url && url.match('facebook')) {
aSubject.cancel(Components.results.NS_BINDING_SUCCEEDED);
}
}
}
}, "http-on-modify-request", false);
Can you take var appcontent = window.document.getElementById("appcontent"); out of the eventlistener and do a setInterval until it actually sees that DOM element get populated?
init();
function init(){
var intval = setInterval ( "checkForElement()", 200 );
}
function checkForElement(){
if (document.getElementById('appcontent') != 'undefined'){
clearInterval(intval);
var appcontent = window.document.getElementById("appcontent");
appcontent.addEventListener("load",onEventLoad,true);
}
}
You might have to do some alert(document.getElementById('appcontent')); debugging to see if each browser treats a null element the same way.
Are you setting the src of your appcontent element as an attribute in the XUL source code? That makes things tricker, as XUL doesn't like you accessing elements while it's loading. What you could try is adding a derived XBL binding to the element, and defining a capturing event handler there. Or you might find that the DOMContentLoaded event is early enough. Otherwise I would suggest leaving the appcontent element blank and only loading it after you've added your load event handler in the main window's load event handler.

Self-closing popups in IE -- how to get proper onBlur behavior?

I want a transient window to close itself when the user clicks away from it. This works for Firefox:
var w = window.open(...);
dojo.connect(w, "onblur", w, "close");
but it doesn't seem to work in Internet Explorer. Some other sites made reference to an IE-specific "onfocusout" event, but I couldn't find a coherent working example of what I need.
What does Stack Overflow say about the best way to get IE browser windows to close when they lose focus?
I'm using Dojo so if there's some shortcut in that library, information would be welcome. Otherwise standard IE calls will be the best answer.
I figured out the alternative in IE.
This:
that.previewWindowAction = function () {
var pw =
window.open(this.link, "preview",
"height=600,width=1024,resizable=yes,"
+ "scrollbars=yes,dependent=yes");
dojo.connect(pw, "onblur", pw, "close");
};
should be written like this to work in IE:
that.previewWindowAction = function () {
var pw =
window.open(this.link, "preview",
"height=600,width=1024,resizable=yes,"
+ "scrollbars=yes,dependent=yes");
if (dojo.isIE) {
dojo.connect
(pw.document,
"onfocusin",
null,
function () {
var active = pw.document.activeElement;
dojo.connect
(pw.document,
"onfocusout",
null,
function () {
if (active != pw.document.activeElement) {
active = pw.document.activeElement;
} else {
window.open("", "preview").close();
}
});
});
}
else {
dojo.connect(pw, "onblur", pw, "close");
}
};
The reasons?
In IE, window objects do not respond to blur events. Therefore we must use the proprietary onfocusout event.
In IE, onfocusout is sent by most HTML elements, so we must add some logic to determine which onfocusout is the one caused by the window losing focus. In onfocusout, the activeElement attribute of the document is always different from the previous value -- except when the window itself loses focus. This is the cue to close the window.
In IE, documents in a new window send an onfocusout when the window is first created. Therefore, we must only add the onfocusout handler after it has been brought into focus.
In IE, window.open does not appear to reliably return a window handle when new windows are created. Therefore we must look up the window by name in order to close it.
Try:
document.onfocusout = window.close();
You might try this as part of an IE-specific code block:
w.onblur = function() { w.close();};

Categories

Resources