How do I delete cross site cookies with Firefox 22+ extension? - firefox

I am attempting to add a function to my Firefox extension to trigger an event to delete cookies from site B when a button on site A is clicked. Site A and B do not share a domain but site B is running in an iframe injected into site A. I need the click event in the Firefox content script to trigger an event either in the content script or the Firefox extension main to delete all of the cookies from site B.
I have the click listener assigned to the button and firing. I have already achieved this same effect in Google Chrome with an extension. I get an error about using components, but I could not find a solution to use instead of components. It only needs to work on Firefox 22+. I am using addon-sdk-1.14 to develop the extension.
ContentScript.js
function DeleteCookies() {
var payload="Delete";
self.port.emit("Delete", payload);
}
Main.js
var {Cc, Ci} = require("chrome");
pageMod.PageMod({
include: "*",
contentScriptFile: [ self.data.url("jquery-1.9.1.js")
,self.data.url("script.js")],
onAttach: function(worker) {
worker.port.on('Delete',function (){ DeleteCookies();});
}
});
function DeleteCookies() {
var cookieManager = Cc["#mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager);
var domain= "siteB.com";
var iter = cookieManager.enumerator;
var cookie_count = 0;
while (iter.hasMoreElements()) {
var cookie = iter.getNext();
if (cookie instanceof Ci.nsICookie) {
if (domain.indexOf(cookie.host.toUpperCase()) != -1) {
cookieManager.remove(cookie.host, cookie.name, cookie.path, cookie.blocked);
cookie_count++;
}
}
}
};

You can't access XPCOM from a content script. Use the port mechanism for communication between the content script and main.js, and do the cookie deletion from the latter.

It appears the code that will correctly perform the task was edited into the question rather than be posted as an answer. Unfortunately, there appears to be a bug and it iterates over all cookies rather than just those in the domain from which you desire to delete. For the question Deleting cookies with JavaScript in Firefox extension, I modified the code in this question to be more efficient, and fixed the bug. Given that I already worked on the code, I am posting it here so that others don't need to go through figuring out that the code was edited into the question and finding the issues with the code.
The bug is that cookie.blocked is passed to cookieManager.remove() when there is no blocked attribute defined for nsICookie. What should be passed there is false, assuming that there is no intent to block cookies from that host. As it is, undefined is currently being passed.
Iterating over only those cookies from the host in question, rather than all cookies, is accomplished by using the nsICookieManager2 interface. Specifically the getCookiesFromHost() method.
The updated code is:
ContentScript.js
function DeleteCookies() {
var payload="Delete";
self.port.emit("Delete", payload);
}
Main.js
var {Cc, Ci} = require("chrome");
pageMod.PageMod({
include: "*",
contentScriptFile: [ self.data.url("jquery-1.9.1.js")
,self.data.url("script.js")],
onAttach: function(worker) {
worker.port.on('Delete',function (){ DeleteCookies();});
}
});
var cookieManager = Cc["#mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
function DeleteCookies() {
DeleteAllCookiesForDomain("siteB.com");
};
function DeleteAllCookiesForDomain( domain ) {
var iter = cookieManager.getCookiesFromHost(domain);
var cookie_count = 0;
while (iter.hasMoreElements()) {
var cookie = iter.getNext();
if (cookie instanceof Ci.nsICookie) {
cookieManager.remove(cookie.host, cookie.name, cookie.path, false);
cookie_count++;
}
}
return cookie_count;
};

Related

pushState change - equivalent to Chrome Extension onHistoryStateUpdated

I'm porting a Chrome extension to a Firefox extension and due to the nature of the website that it runs on, I need to monitor the pushState.
Chrome Extensions has a handy way to handle this: chrome.webNavigation.onHistoryStateUpdated. The way that I use it in the Chrome extension is as follows:
chrome.webNavigation.onHistoryStateUpdated.addListener(function(details) {
var tabUrl = details.url;
if (isTabUrlValid(tabUrl)) {
$.get(tabUrl, function(data) {
var videoUrl = $(data).find('meta[itemprop=contentURL]').prop('content');
videoUrl = validateUrl(videoUrl);
videoUrl5k = make5kUrl(videoUrl);
});
}
});
I need to do the same thing for the Firefox Extension, but I haven't found any good answers. I've tried doing the answer mentioned here: How to get notified about changes of the history via history.pushState?
(function(history) {
var pushState = history.pushState;
history.pushState = function(state) {
if (typeof history.onpushstate == "function") {
history.onpushstate({state: state});
}
var tabUrl = tabs.activeTab.url;
console.log("UPDATED TAB URL: " + tabUrl);
if (isTabUrlValid(tabUrl)) {
$.get(tabUrl, function(data) {
var videoUrl = $(data).find('meta[itemprop=contentURL]').prop('content');
videoUrl = validateUrl(videoUrl);
videoUrl5k = make5kUrl(videoUrl);
});
}
return pushState.apply(history, arguments);
};
})(window.history);
The problem is that when I do cfx run it complains that history/window is undefined and therefore never gets detected. I think this is due to it being within the SDK, but I don't know of a good workaround.
Any thoughts?
Edit: I looked at #willma's answer below and I don't think that would work for me. The issue is that the URL is updated via pushState and the DOM is not... Is there any good way replicate what I do in the chrome extension?
Edit: Here's the pageMod portion
pageMod.PageMod({
attachTo: 'top', // Don't attach to iFrames --> http://goo.gl/b6b1Iv
include: [URLs],
contentScriptFile: [data.url("jquery-2.1.1.min.js"),
data.url("csScript.js")],
onAttach: function(worker) {
worker.port.on('url', function(url) {
var videoUrl = validateUrl(url);
videoUrl5k = make5kUrl(videoUrl);
console.log("--5K URL--: " + videoUrl5k);
});
}
});
That history code needs to get injected into a tab using a content script. Right now your logic says when the history event occurs, check to see if the tab URL is valid.
In Firefox, the logic will be the other way around: when a tab is opened, check if its URL is valid, and if so, then attach a script to it that will monitor for the history event. To do so you'll need to use a Page Mod.
Edit: All the code
One key concept you're missing is the difference between a content script and a main/library script. The library scripts are stored in lib and have access to all the SDK modules, but don't have access to the DOM, window object… The content scripts are stored in data, are injected into a page using the PageMod or tabs modules, can access the dom and window objects, but have no access to any SDK modules. Content scripts are essentially like the page scripts you'd attach your standard HTML page (with <script></script>) with the caveats that they can't share variables other page scripts but they can communicate with the main scripts.
The only reason I bring this up is because your initial problem was trying to access the window object from a main script and the problem in your fiddle is that you're trying to access the tabs module inside a content script. It's worth reading the topmost link in this answer if this is still confusing.
main.js
const { PageMod } = require('sdk/page-mod');
var sendXHR = function(url) {
// Do something with the new URL
// See Request Module docs (below) for sending XHRs from main script.
}
const pageMod = PageMod({
attachTo: 'top',
include: '*',
onAttach: function(worker) {
worker.port.on('newURL', sendXHR);
}
});
content.js
var sendNewUrlToMain = function() {
self.port.emit('newURL', location.href);
}
var pushState = window.history.pushState;
window.history.pushState = function(state) {
if (typeof history.onpushstate == "function") {
history.onpushstate({state: state});
}
sendNewUrlToMain();
return pushState.apply(history, arguments);
}
window.addEventListener('hashchange', sendNewUrlToMain);
Here are the request module docs, for making XHRs.
NB: if you don't want to use the request module (the only reason being that you already have standard XHR code for your chrome extension and don't want to take the time to learn/rewrite that code), you can send a standard XHR from the content script, but in doing so, you risk allowing the user to close the tab and thus destroy the script before your XHR callbacks are executed.

Firefox Bootstrapped Add-On Injecting before load

Is it possible to add event listeners for a document before a page has been navigated to using a Bootstrapped add-on? I would like to see what page the user wants to navigate to as well as later after the page loads to inspect the DOM. I need to run code in the HTML content context.
In the past I used a toolbar XUL and included javascript within it and it would load before the HTML page loaded.
i looked into doing stuff before DOMContentLoaded sometime ago and found out there is a document inserted observer.
order of events after running research code at bottom
readystate changes to interactive (i think multiple times, not sure)
readystate changes to complete
DOMContentLoaded event fires
load event fires (Sometimes load doesnt fire, if you might have to change addEventListener with capture arugment (3rd argument) as false or true)
apparently there should be readystate loading before all of this but i can never catch it i dont know why.
after running the code in scratchpad, browser environemnt of course, then load a new page and watch the error console it will throw these reports in this order:
ready state changed! ("interactive") Scratchpad/4:18
02:28:07.873 ready state changed! ("complete") Scratchpad/4:18
02:28:07.874 DOMContentLoaded event fired! Scratchpad/4:53
02:28:07.938 Load event fired! Scratchpad/4:45
here is the research code. it adds a the listeners and observer to see whats firing.
var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
var os = Cc['#mozilla.org/observer-service;1'].getService(Ci.nsIObserverService);
var LMObserver;
function myObserver() {
this.register();
}
myObserver.prototype = {
observe: function (subject, topic, data) {
//Cu.reportError(subject);
//Cu.reportError(data);
//i think subject is window element
subject.onreadystatechange = function () {
//loading
//interactive
//complete
Cu.reportError('ready state changed! ("' + subject.readyState + '")');
//var body = subject.documentElement.querySelector('body')
//you want to change title so you would do that here do something like: if (subject.readystate == 'complete') { subject.title = 'blah blah' }
//Cu.reportError('has body element: ' + body)
}
},
register: function () {
os.addObserver(this, 'document-element-inserted', false);
},
unregister: function () {
os.removeObserver(this, 'document-element-inserted', false);
}
};
//below this is the DOMContentLoaded thing i put this here so we can see what fires in what order
var pageLoad = function(event) {
var win = event.originalTarget.defaultView;
if (win && win.frameElement) {
return;
}
Cu.reportError('Load event fired!');
}
var pageDOMContentLoaded = function(event) {
var win = event.originalTarget.defaultView;
if (win && win.frameElement) {
return;
}
Cu.reportError('DOMContentLoaded event fired!');
}
LMObserver = new myObserver;
gBrowser.addEventListener("load", pageLoad, true);
gBrowser.addEventListener("DOMContentLoaded", pageDOMContentLoaded, true);
//gBrowser.removeEventListener("load", pageLoad, true);
//gBrowser.removeEventListener("DOMContentLoaded", pageDOMContentLoaded, true);
//LMObserver.unregister();
Here's some more indepth research on load events added with true or false as capture argument: https://github.com/Noitidart/event-listener-experiment-DOMC-and-load/blob/master/bootstrap.js

iScroll Ajax content

i have problem with refreshing content in wrapper after it is loaded by ajax.
When i check with firebug - XHR is showing request and i can see elements loaded but it isn't showing on page.
This is what i am using for pullDown function to get ajax content
function pullDownAction () {
setTimeout(function () {
var el, li, i;
el = document.getElementById('thelist');
var http = new XMLHttpRequest();
var url = window.location;
http.open("GET",url,true);
http.send();
myScroll.destroy();
myScroll = null;
loaded();
}, 1000);
}
It looks like as content is stuck between showing on webpage and ajax request.
Any idea?
myScroll.refresh() (instead of .destroy() and recalling "loaded()") should do the trick!
If you're using IScroll4 you can try to use the checkDOMChanges:true option of iscroll.
If it still won't work - it could be a CSS issue caused by the scroll-wrapper (#scroller) not expanding with its content. (float,position:absolute; or something like that)
EDIT: it seems to me as you're not handling a responseText of the request at all!
According to this example you need an event handler for the onreadystatechange event:
http.open("GET",url,true);
http.onreadystatechange = function () {
if (http.readyState == 4) {
alert(http.responseText); //handle this response! (i.e. writing to an element's innerHTML)
}
};
http.send(null);

Event Listeners to know when a page is Bookmarked/Downloaded

Is there any way to track when a page is bookmarked or downloaded in Firefox? I
mean is there any event that is triggered on bookmarking or
downloading a page? I am using Add-on SDK for developing Add-on.
If not, then kindly suggest me some workarounds.
The browser window has <command> elements that get triggered when the user bookmarks or downloads a page. The former has ID Browser:AddBookmarkAs, the latter Browser:SavePage. The Add-on SDK itself doesn't give you access to them, so you need to use the chrome package to access XPCOM directly. Something like this:
// Add listener to all existing browser windows
var {Cc, Ci} = require("chrome");
var mediator = Cc["#mozilla.org/appshell/window-mediator;1"]
.getService(Ci.nsIWindowMediator);
var enumerator = mediator.getEnumerator("navigator:browser");
while (enumerator.hasMoreElements())
listenToWindow(enumerator.getNext().QueryInterface(Ci.nsIDOMWindow));
// Get notified when new browser windows open
var observers = require("observer-service");
observers.add("chrome-document-global-created", function(window)
{
if (window instanceof Ci.nsIDOMWindow && window.location.href == "chrome://browser/content/browser.xul")
listenToWindow(window);
});
function listenToWindow(window)
{
window.document
.getElementById("Browser:AddBookmarkAs")
.addEventListener("command", onBookmark, false);
window.document
.getElementById("Browser:SavePage")
.addEventListener("command", onSavePage, false);
}
This code isn't tested so there might be minor issues but the overall concept should be correct.
Edit: Actually, the same seems to be simpler if you use the internal window-utils package. Not sure whether the API provided by this package is stable however.
var windows = require("window-utils");
for (window in windows.browserWindowIterator)
listenToWindow(window);
var observers = require("observer-service");
observers.add("chrome-document-global-created", function(window)
{
if (window instanceof Ci.nsIDOMWindow && windows.isBrowser(window))
listenToWindow(window);
});
It is all in the addon sdk documentation. Although I must admit I did not see it the first time around.
https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/tutorials/event-targets.html
The following example is from the documentation.
Note that I had to add Cr to the require to make it work
as well as substitute Components.interfaces by Ci in the generateQI() call.
var {Cc, Ci, Cu, Cr} = require("chrome");
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
var bookmarkService = Cc["#mozilla.org/browser/nav-bookmarks-service;1"]
.getService(Ci.nsINavBookmarksService);
var bookmarkObserver = {
onItemAdded: function(aItemId, aFolder, aIndex) {
console.log("added ", bookmarkService.getBookmarkURI(aItemId).spec);
},
onItemVisited: function(aItemId, aVisitID, time) {
console.log("visited ", bookmarkService.getBookmarkURI(aItemId).spec);
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver])
};
exports.main = function() {
bookmarkService.addObserver(bookmarkObserver, false);
};
exports.onUnload = function() {
bookmarkService.removeObserver(bookmarkObserver);
}

Ajax Broken in Browsers works in Android

I can run this code in Android app (using PhoneGap adn jQuery Mobile) but not on desktop browsers.
It gives me a syntax error in firebug for this line =
var TicketList = eval("(" + ajax.responseText + ")");
Here is the code
// JScript source code
// ran on body load
function doJsStuff()
{
var ajax = AJAX();
ajax.onreadystatechange = function () {
if (ajax.readyState == 4) {
var TicketList = eval("(" + ajax.responseText + ")");
if (TicketList.ListCount > 0) {
document.getElementById("opencount").innerHTML = TicketList.ListCount +" Open Tickets";
for (Ticket in TicketList.Tickets) {
// add stuff to DOM
//AddTicketToList(TicketList.Tickets[Ticket]);
}
}
else {
document.getElementById("opencount").innerHTML = "All Tickets Reviewed";
DisplayNoresults();
}
}
}
ajax.open("GET", "http://website.com/ListTicketsRequest.ashx?PageNumber=1&PageSize=1&Status=Open", true);
ajax.send(null);
//document.addEventListener("deviceready", onDeviceReady, false);
//event to check for PhoneGap
//$('ul').listview('refresh');
$('#mtickets').page();
//showVars();
}
function AJAX()
{
var xmlHttp;
try
{
xmlHttp = new XMLHttpRequest();
}
catch (e)
{
}
return xmlHttp;
}
**TicketList is a variable in the JSon that comes across like this=
{"Tickets" : [{"TicketID": "1054","Category": "N/A","SubmittedUserID": "bob.thebuilder","ShortDescription": "test question QID:16668","CreationDate": "2/16/2011 12:24:19 PM","TicketStatus": "Open","LongDescription": "Something is wrong with this question I know I hve the right answer but it keeps telling me I'm wrong"},{"TicketID": "1053","Category": "Mission Support","SubmittedUserID": "dave","ShortDescription": "Make courseware revisions","CreationDate": "2/16/2011 9:34:48 AM","TicketStatus": "Open","LongDescription": "Find help tickets generated by users for possible courseware update."}], "PageCount": "6", "ListCount": "11"}
Note about PhoneGap If you are trying to include phoengap functions in a place where the code may also be executed on in a browser make sure you only add the phone gap function with on "deviceready" or your browser will not render. Example:
function onload(){
//event to check for PhoneGap
document.addEventListener("deviceready", onDeviceReady, true);
}
...
function onDeviceReady()
{
// Now PhoneGap API ready
vibrate(90); // vib to ack pg ready
$("a").click(function(event){
vibrate(30); // add 30 sec vib to all links
});
}
My immediate response would be to use jQuery's getJSON method, since you're aready using jQuery. jQuery's AJAX provides a much broader base of browser compatibility. Also, every time you use eval(), a small baby somewhere cries.
var url = "http://website.com/ListTicketsRequest.ashx?PageNumber=1&PageSize=1&Status=Open";
$.getJSON(url ,function(TicketList){
if (TicketList.ListCount > 0) {
$("#opencount").html(TicketList.ListCount +" Open Tickets");
for (Ticket in TicketList.Tickets) {
...
}
} else {
$("#opencount").html("All Tickets Reviewed");
DisplayNoresults();
}
});
If this still doesn't work for you, ensure that the JSON being returned is valid. But please stick to this method, and don't use eval!!
SIMPLIFIED UPDATE
var url = "http://website.com/ListTicketsRequest.ashx?PageNumber=1&PageSize=1&Status=Open";
$.getJSON(url ,function(AnyNameYouWant){
alert(AnyNameYouWant.ListCount + " Open Tickets");
});
UPDATE USING 'DATA'
If your url becomes too long, you might begin to encounter problems. It is suggested to pass the url data via the data argument.
var url = "http://website.com/ListTicketsRequest.ashx";
var data = "PageNumber=1&PageSize=1&Status=Open";
$.getJSON(url, data, function(AnyNameYouWant){
alert(AnyNameYouWant.ListCount + " Open Tickets");
});
Looking at your code, it seems likely to me that the syntax error isn't in the code you posted, but instead is contained in the JSON object you're evaluating in ajax.responseText. Take a look at the data being returned by the AJAX request. Is it valid Javascript? Does the page you're calling return something different to desktop browsers vs mobile? Is there an error message where the JSON code should be?
Another possibility: Is your app running on website.com? If not, Firefox is probably blocking the XMLHttpRequest from functioning properly. Firefox 3 and below block cross-site AJAX requests. Firefox 3.5 seems to allow some exceptions.

Resources