Simulate drop event with a file - firefox-addon

Users upload images to this form by dropping a file on it.
Is there a way in to trigger drop with a File/Blob object programmatically? Something like MouseEvent.initMouseEvent() https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/initMouseEvent
but like initDropEvent?
I am researching here:
https://dxr.mozilla.org/mozilla-central/source/obj-x86_64-unknown-linux-gnu/_tests/testing/mochitest/mochijar/chrome/mochikit/content/tests/SimpleTest/ChromeUtils.js?offset=200#254
https://dxr.mozilla.org/mozilla-central/source/browser/base/content/test/newtab/head.js?offset=0#690
https://dxr.mozilla.org/mozilla-central/source/browser/base/content/test/newtab/browser_newtab_bug765628.js#25
https://dxr.mozilla.org/mozilla-central/source/testing/mochitest/tests/SimpleTest/ChromeUtils.js#179 ----- this one is a mochitest and code from mochitests never ever work for me, so i think this one is useless as it needs some special environment i think
Based on this I ran this code from scratchpad witha tab with twitter loaded, and aftre clicking the "tweet" button:
// https://dxr.mozilla.org/mozilla-central/source/browser/base/content/test/newtab/head.js?offset=100#690
//https://dxr.mozilla.org/mozilla-central/source/browser/base/content/test/newtab/browser_newtab_bug765628.js#21
/**
* Creates a custom drag event.
* #param aEventType The drag event's type.
* #param aData The event's drag data (optional).
* #return The drag event.
*/
function createDragEvent(aContentWindow, aEventType, aData, aDataType) {
// aDataType text/x-moz-url, text/plain, etc
let dataTransfer = new (aContentWindow).DataTransfer("dragstart", false);
dataTransfer.mozSetDataAt(aDataType, aData, 0);
let event = aContentWindow.document.createEvent("DragEvents");
event.initDragEvent(aEventType, true, true, aContentWindow, 0, 0, 0, 0, 0,
false, false, false, false, 0, null, dataTransfer);
return event;
}
function sendTwitterDropEvent() {
var aContentWindow = gBrowser.contentWindow;
var aContentDocument = aContentWindow.document;
var btnNewTweet = aContentDocument.getElementById('global-new-tweet-button');
console.info('btnNewTweet:', btnNewTweet);
if (!btnNewTweet) {
throw new Error('global tweet button not found, probably not logged in');
}
btnNewTweet.click();
var inputAddPhoto = aContentDocument.getElementById('global-tweet-dialog').querySelector('input[type=file]');
if (!inputAddPhoto) {
throw new Error('add photo button not found! i have no idea what could cause this');
}
var formTweet = aContentDocument.getElementById('global-tweet-dialog-dialog').querySelector('form'); //<form.t1-form.tweet-form has-preview has-thumbnail dynamic-photos photo-square-4>
if (!formTweet) {
throw new Error('tweet form not found! i have no idea what could cause this');
}
console.info('formTweet:', (formTweet instanceof Ci.nsIDOMNode));
var ifaceReq = aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor);
var windowUtils = ifaceReq.getInterface(Ci.nsIDOMWindowUtils);
var aDragData = 'site 99';
var aDragDataType = 'plain/text';
var event = createDragEvent(aContentWindow, "drop", aDragData, aDragDataType);
windowUtils.dispatchDOMEventViaPresShell(formTweet, event, true);
}
sendTwitterDropEvent();
This should populate the tweet input with "site 99" but it doesnt. I was testing with plain text then after I get that working I was thinking of moving to File/Blob.
Simplified non-twitter test case
Open tab with this: data:text/html,<input id=rawr>
Open scratchpad and run this code from scratchpad:
function createDragEvent(aContentWindow, aEventType, aData, aDataType) {
// aDataType text/x-moz-url, text/plain, etc
let dataTransfer = new (aContentWindow).DataTransfer("dragstart", false);
dataTransfer.mozSetDataAt(aDataType, aData, 0);
let event = aContentWindow.document.createEvent("DragEvents");
event.initDragEvent(aEventType, true, true, aContentWindow, 0, 0, 0, 0, 0,
false, false, false, false, 0, null, dataTransfer);
return event;
}
function sendTwitterDropEvent() {
var aContentWindow = gBrowser.contentWindow;
var aContentDocument = aContentWindow.document;
var formTweet = aContentDocument.getElementById('rawr');
if (!formTweet) {
throw new Error('tweet form not found! i have no idea what could cause this');
}
console.info('formTweet:', (formTweet instanceof Ci.nsIDOMNode), formTweet);
var ifaceReq = aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor);
var windowUtils = ifaceReq.getInterface(Ci.nsIDOMWindowUtils);
var aDragData = 'site 99';
var aDragDataType = 'plain/text';
var event = createDragEvent(aContentWindow, "drop", aDragData, aDragDataType);
var rezDrop = windowUtils.dispatchDOMEventViaPresShell(formTweet, event, true);
console.info('rezDrop:', rezDrop);
}
sendTwitterDropEvent();

Related

Firefox addon which refreshes the tap and auto resend data

I'm trying make a firefox add-on, which reloads a page automatically under some conditions.
First I add a contentScript to a tab to get some information on the page in my addon
tab.attach({
contentScript:self.port.emit...
I have it working to the point of the refresh with
tab.reload();
but then an alert pops up every time "if it should resend data".
I want to resend the data automatically.
How and where do I do it? In the add-on or in the contentScript?
Has it to do with the Load Flags constant?
This clicks the accept button when the prompt opens. But you see the prompt window for like a couple hundred ms.
i had to use setTimeout with 0 wait time other wise the aDOMWindow.args property and aDOMWindow.Dialog and a bunch of other stuff would be undefined or null so weird. But this works:
var stringBundle = Services.strings.createBundle('chrome://browser/locale/appstrings.properties');
try {
windowListener.unregister();
} catch (ignore) {}
var windowListener = {
//DO NOT EDIT HERE
onOpenWindow: function(aXULWindow) {
// Wait for the window to finish loading
let aDOMWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
aDOMWindow.addEventListener('load', function() {
aDOMWindow.removeEventListener('load', arguments.callee, false);
windowListener.loadIntoWindow(aDOMWindow, aXULWindow);
}, false);
},
onCloseWindow: function(aXULWindow) {},
onWindowTitleChange: function(aXULWindow, aNewTitle) {},
register: function() {
// Load into any existing windows
let XULWindows = Services.wm.getXULWindowEnumerator(null);
while (XULWindows.hasMoreElements()) {
let aXULWindow = XULWindows.getNext();
let aDOMWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
windowListener.loadIntoWindow(aDOMWindow, aXULWindow);
}
// Listen to new windows
Services.wm.addListener(windowListener);
},
unregister: function() {
// Unload from any existing windows
let XULWindows = Services.wm.getXULWindowEnumerator(null);
while (XULWindows.hasMoreElements()) {
let aXULWindow = XULWindows.getNext();
let aDOMWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
windowListener.unloadFromWindow(aDOMWindow, aXULWindow);
}
//Stop listening so future added windows dont get this attached
Services.wm.removeListener(windowListener);
},
//END - DO NOT EDIT HERE
loadIntoWindow: function(aDOMWindow, aXULWindow) {
if (!aDOMWindow) {
return;
}
if (aDOMWindow.location == 'chrome://global/content/commonDialog.xul') {
var repostString = stringBundle.GetStringFromName('confirmRepostPrompt');
var repostStringFormatted = stringBundle.formatStringFromName('confirmRepostPrompt', [aDOMWindow.Application.name], 1);
aDOMWindow.setTimeout(function() {
console.log('setimeout val 00:', aDOMWindow.args)
//aDOMWindow.args and aDOMWindow.Dialog is not available till after setTimeout of 0 so weird
if (aDOMWindow.args.text == repostString || aDOMWindow.args.text == repostStringFormatted) {
console.log('this is resend prompt so accept it');
//aDOMWindow.Dialog.ui.button0.click(); //doesnt work
//aDOMWindow.Dialog.onButton0(); // doesnt work
//aDOMWindow.ondialogaccept(); //doesnt work
var dialog = aDOMWindow.document.getElementById('commonDialog');
var btnAccept = aDOMWindow.document.getAnonymousElementByAttribute(dialog, 'dlgtype', 'accept');
btnAccept.click();
console.log('clicked');
}
}, 0);
}
},
unloadFromWindow: function(aDOMWindow, aXULWindow) {
if (!aDOMWindow) {
return;
}
}
};
windowListener.register();
open scratchpad. set environemnt to browser. run the code. refresh a page to get prompt you'll see it get clicked.

saving file to IOS device using air

I've been trying to save file from server to an ios device using urlstream but it doesn't work (it works fine on android devices . I tried using(documentsDirectory) but it doesn't work too .I used many other methods like (file.download ) and others but none is working . Any help please
I am using flash pro cs6 .
script sample :
import flash.filesystem.*;
import flash.events.ProgressEvent;
var urlString:String = "http://example.sample.mp3";
var urlReq:URLRequest = new URLRequest(urlString);
var urlStream:URLStream = new URLStream();
var fileData:ByteArray = new ByteArray();
urlStream.addEventListener(Event.COMPLETE, loaded);
urlStream.addEventListener(ProgressEvent.PROGRESS, progressHandler);
urlStream.load(urlReq);
function loaded(event:Event):void {
urlStream.readBytes(fileData, 0, urlStream.bytesAvailable);
writeAirFile();
}
function writeAirFile():void {
var file:File = File.applicationStorageDirectory.resolvePath("sample.mp3");
var fileStream:FileStream = new FileStream();
fileStream.open(file, FileMode.WRITE);
fileStream.writeBytes(fileData, 0, fileData.length);
fileStream.close();
trace("The file is written.");
}
function progressHandler(event:Event):void {
trace ("progressHandler: " + event);
}
Tested on iOS
_urlString = "http://example.sample.mp3";
_urlReq = new URLRequest(_urlString);
_urlStream = new URLStream();
_urlStream.addEventListener(flash.events.ProgressEvent.PROGRESS, progressHandler, false, 0, true);
_urlStream.addEventListener(flash.events.Event.COMPLETE, saveFileToDisc, false, 0, true);
_urlStream.addEventListener(flash.events.IOErrorEvent.IO_ERROR, errorHandler, false, 0, true);
_urlStream.load(_urlReq);
private function progressHandler(evt:flash.events.ProgressEvent):void {
trace("progress: " + event.target.progress);
}
private function errorHandler(evt:flash.events.IOErrorEvent):void {
//do something
}
private function saveFileToDisc(event:flash.events.Event):void {
_fileData = new ByteArray();
_urlStream.readBytes(_fileData, 0, _urlStream.bytesAvailable);
_file = File.applicationStorageDirectory.resolvePath("sample.mp3");
_file.preventBackup = true;
_writeFileStream.addEventListener(flash.events.IOErrorEvent.IO_ERROR, filestreamErrorHandler, false, 0, true);
_writeFileStream.addEventListener(flash.events.Event.CLOSE, fileSaved, false, 0, true);
_writeFileStream.openAsync(_file, FileMode.UPDATE);
_writeFileStream.writeBytes(_fileData, 0, _fileData.length);
_writeFileStream.close();
}
private function filestreamErrorHandler(evt:flash.events.IOErrorEvent):void {
//do something
}
private function fileSaved(closeEvent:flash.events.Event):void {
//trace("file saved");
_writeFileStream.removeEventListener(flash.events.IOErrorEvent.IO_ERROR, filestreamErrorHandler);
_writeFileStream.removeEventListener(flash.events.Event.CLOSE, fileSaved);
_urlString = null;
_urlReq = null;
_urlStream = null;
_file = null;
_fileData.length = 0;
_fileData = null;
}
Two obvious problems:
This line: urlStream..addEventListener(ProgressEvent.PROGRESS, progressHandler); has ' .. ' which is wrong.
Also you never write anything into your ByteArray.
set the 3rd parameter to 0 to make sure to read the entire data:
urlStream.readBytes(fileData, 0, 0); //0 = read all
Besides URLStream is not meant for that type of operation (it is meant to stream the loading of binary data). I personally do this using URLLoader (loading in binary) and everything works perfectly and save copy on folder.

Setting Context Item position in Firefox addons SDK

I'm writing an extension that involving adding an item to Firefox's context menu, but it appends to the end of the menu and I couldn't find any pointers customizing item's position using Addon SDK (insertBefore/insertAfter), I know how this can be done using XUL, but I'm trying to do it using Addon SDK or some sort of Addon SDK/XUL combination
This is the code snippet related to context menu
main.js
var pageMod = require("sdk/page-mod");
var data = require("sdk/self").data;
var tabs = require("sdk/tabs");
var cm = require("sdk/context-menu");
pageMod.PageMod({
include: "*.youtube.com",
contentScriptFile: data.url("page.js"),
onAttach: function (worker) {
worker.port.emit('link', data.url('convertbutton.png'));
}});
cm.Item({
label: "Convert File",
image: data.url("bighdconverterlogo128png.png"),
context: [
cm.URLContext(["*.youtube.com"]),
cm.PageContext()
],
contentScriptFile: data.url("menu.js"),
onMessage: function(vUrl){
tabs.open(vUrl);
}
});
data/menu.js
self.on("click", function(){
self.postMessage('http://hdconverter.co/' + 'c.php?url=' + window.location.href);
});
Thanks
i dont know about sdk but for non-sdk addons its easy. but because you dont have the boiler plate setup its going to look long. add this code to your addon at the bottom:
var positionToInsertMenu = 0; //set the position you want it at here
var myLabelText = 'Convert File';
const {interfaces: Ci,utils: Cu} = Components;
Cu.import('resource://gre/modules/Services.jsm');
/*start - windowlistener*/
var windowListener = {
//DO NOT EDIT HERE
onOpenWindow: function (aXULWindow) {
// Wait for the window to finish loading
let aDOMWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
aDOMWindow.addEventListener("load", function () {
aDOMWindow.removeEventListener("load", arguments.callee, false);
windowListener.loadIntoWindow(aDOMWindow, aXULWindow);
}, false);
},
onCloseWindow: function (aXULWindow) {},
onWindowTitleChange: function (aXULWindow, aNewTitle) {},
register: function () {
// Load into any existing windows
let XULWindows = Services.wm.getXULWindowEnumerator(null);
while (XULWindows.hasMoreElements()) {
let aXULWindow = XULWindows.getNext();
let aDOMWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
windowListener.loadIntoWindow(aDOMWindow, aXULWindow);
}
// Listen to new windows
Services.wm.addListener(windowListener);
},
unregister: function () {
// Unload from any existing windows
let XULWindows = Services.wm.getXULWindowEnumerator(null);
while (XULWindows.hasMoreElements()) {
let aXULWindow = XULWindows.getNext();
let aDOMWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
windowListener.unloadFromWindow(aDOMWindow, aXULWindow);
}
//Stop listening so future added windows dont get this attached
Services.wm.removeListener(windowListener);
},
//END - DO NOT EDIT HERE
loadIntoWindow: function (aDOMWindow, aXULWindow) {
if (!aDOMWindow) {
return;
}
var contentAreaContextMenu = aDOMWindow.document.getElementById('contentAreaContextMenu');
var myMenuItem;
if (contentAreaContextMenu) {
var menuItems = contentAreaContextMenu.querySelector('menuitem');
[].forEach.call(menuItems, function(item) {
if (item.getAttribute('label') == myLabelText) {
myMenuItem = item;
}
});
contentAreaContextMenu.removeChild(myMenuItem);
if (contentAreaContextMenu.childNodes.length >= positionToInsertMenu) { //position is greater then number of childNodes so append to end
contentAreaContextMenu.appendChild(myMenuItem);
} else {
contentAreaContextMenu.insertBefore(myMenuItem, contentAreaContextMenu.childNodes[thePosition]);
}
}
},
unloadFromWindow: function (aDOMWindow, aXULWindow) {
if (!aDOMWindow) {
return;
}
var myMenuItem = aDOMWindow.document.getElementById('myMenuItem');
if (myMenuItem) {
myMenuItem.parentNode.removeChild(myMenuItem);
}
}
};
windowListener.register();
on unload of your addon add this:
windowListener.unregister();
i copied pasted from a template and modded it real fast. for position to be accurate you probably have to consider which menuitems are hidden and which are not

JQuery dialog box issue in Internet Explorer

I am using a UI dialog box to display a message.
It works well in Firefox and Google Chrome. However, when I test in IE versions the dialog box doesn't open.
Can any one tell what real problem is?
I had pasted my code below:
function check_selected(c) {
var count = c - 1;
var radios = document.getElementsByName('plan');
for ( var i = 0; i < radios.length; i++) {
if (radios[i].disabled) {
if (radios[i].checked) { // checked
$('#planalert').dialog({
modal : true,
autoOpen : true,
title : "Plan",
width : 650,
height : 150,
show : "blind",
hide : "scale",
});
var c = 0;
} else {
var c = 1;
}
}
}
;
if (c == 0) {
return false;
} else {
return true;
}
}
Try removing the trailing comma from the options object you're passing dialog:
$('#planalert').dialog({
modal:true,
autoOpen: true,
title:"Plan",
width:650,
height:150,
show: "blind",
hide: "scale" // <-----
});
Internet Explorer will choke on the extra comma, while other browsers may not.
Also, remove the semicolon (;) at the end of the for loop ending brace:
for (var i = 0; i < radios.length; i++) {
/* snip */
} // <--- semicolon not necessary
Additionally, you are attempting to define c multiple times inside of your function. You are passing it in to the function so your var c = ... statements actually aren't having the effect you intend. You should either use another variable (which I would recommend rather than mutating the parameter you are passed), or remove the var statements from inside the if block.
Additionally, your loop is really only setting c for the last, disabled radio button. Is this what you intended?
Anyway, here is how I would re-write it (without attempting to fix the logical error above). Be sure to use tools like JsHint to check your JavaScript for probems:
function check_selected(c){
var count=c-1;
var radios = document.getElementsByName('plan');
var isChecked = 0;
for (var i = 0; i < radios.length; i++) {
if (radios[i].disabled) {
if (radios[i].checked){ // checked
$('#planalert').dialog({
modal:true,
autoOpen: true,
title:"Plan",
width:650,
height:150,
show: "blind",
hide: "scale"
});
isChecked = 0;
} else{
isChecked = 1;
}
}
}
return isChecked;
}

An observer for page loads in a custom xul:browser

In my firefox extension I'm creating a xul:browser element. I want to have an observer that intercepts any url changes within the embedded browser and opens the url in a new browser tab (in the main browser). I'd also like new windows spawned by the xul:browser window to open in a tab instead of a new browser window.
I've created an observer which works, but I don't yet know how to apply that observer only to the xul:browser element.
function myFunction(){
var container = jQuery("#container")[0];
var new_browser_element = document.createElement('browser');
container.appendChild(new_browser_element);
var observerService = Components.classes["#mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
observerService.addObserver(myObserver, "http-on-modify-request", false);
}
var myObserver = {
observe: function(aSubject, aTopic, aData){
if (aTopic != 'http-on-modify-request'){
aSubject.QueryInterface(Components.interfaces.nsIHttpChannel);
// alert(aSubject.URI.spec);
// Now open url in new tab
}
},
QueryInterface: function(iid){
if (!iid.equals(Components.interfaces.nsISupports) &&
!iid.equals(Components.interfaces.nsIObserver))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
}
};
You could try:
var myObserver = {
observe: function(aSubject, aTopic, aData){
if (aTopic == 'http-on-modify-request')
{
aSubject.QueryInterface(Components.interfaces.nsIHttpChannel);
var url = aSubject.URI.spec;
var postData ;
if (aSubject.requestMethod.toLowerCase() == "post")
{
var postText = this.readPostTextFromRequest(request);
if (postText)
{
var dataString = parseQuery(postText);
postData = postDataFromString(dataString);
}
}
var oHttp = aSubject.QueryInterface(Components.interfaces.nsIHttpChannel);
var interfaceRequestor = oHttp.notificationCallbacks.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
var DOMWindow = interfaceRequestor.getInterface(Components.interfaces.nsIDOMWindow);
//check if it is one of your mini browser windows
if (jQuery(DOMWindow).hasClass('mini_browser'))
{
openInTab(url, postData);
var request = aSubject.QueryInterface(Components.interfaces.nsIRequest);
request.cancel(Components.results.NS_BINDING_ABORTED);
}
}
},
QueryInterface: function(iid){
if (!iid.equals(Components.interfaces.nsISupports) &&
!iid.equals(Components.interfaces.nsIObserver))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
},
readPostTextFromRequest : function(request) {
var is = request.QueryInterface(Components.interfaces.nsIUploadChannel).uploadStream;
if (is)
{
var ss = is.QueryInterface(Components.interfaces.nsISeekableStream);
var prevOffset;
if (ss)
{
prevOffset = ss.tell();
ss.seek(Components.interfaces.nsISeekableStream.NS_SEEK_SET, 0);
}
// Read data from the stream..
var charset = "UTF-8";
var text = this.readFromStream(is, charset, true);
// Seek locks the file so, seek to the beginning only if necko hasn't read it yet,
// since necko doesn't seek to 0 before reading (at lest not till 459384 is fixed).
if (ss && prevOffset == 0)
ss.seek(Components.interfaces.nsISeekableStream.NS_SEEK_SET, 0);
return text;
}
else {
dump("Failed to Query Interface for upload stream.\n");
}
}
return null;
},
readFromStream : function(stream, charset, noClose)
{
var sis = Components.classes["#mozilla.org/binaryinputstream;1"]
.getService(Components.interfaces.nsIBinaryInputStream);
sis.setInputStream(stream);
var segments = [];
for (var count = stream.available(); count; count = stream.available())
segments.push(sis.readBytes(count));
if (!noClose)
sis.close();
var text = segments.join("");
return text;
}
};
function openInTab(url, postData)
{
var wm = Components.classes["#mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
var recentWindow = wm.getMostRecentWindow("navigator:browser");
if (recentWindow)
{
// Use an existing browser window, open tab and "select" it
recentWindow.gBrowser.selectedTab = recentWindow.gBrowser.addTab(url, null, null, postData);
}
}
function parseQuery() {
var qry = this;
var rex = /[?&]?([^=]+)(?:=([^&#]*))?/g;
var qmatch, key;
var paramValues = {};
// parse querystring storing key/values in the ParamValues associative array
while (qmatch = rex.exec(qry)) {
key = decodeURIComponent(qmatch[1]);// get decoded key
val = decodeURIComponent(qmatch[2]);// get decoded value
paramValues[key] = val;
}
return paramValues;
}
function postDataFromString(dataString)
{
// POST method requests must wrap the encoded text in a MIME
// stream
var stringStream = Components.classes["#mozilla.org/io/string-input-stream;1"]
.createInstance(Components.interfaces.nsIStringInputStream);
if ("data" in stringStream) // Gecko 1.9 or newer
stringStream.data = dataString;
else // 1.8 or older
stringStream.setData(dataString, dataString.length);
var postData = Components.classes["#mozilla.org/network/mime-input-stream;1"].
createInstance(Components.interfaces.nsIMIMEInputStream);
postData.addHeader("Content-Type", "application/x-www-form-urlencoded");
postData.addContentLength = true;
postData.setData(stringStream);
return postData;
}
I'll update this to fill in the blanks in a bit.
edit: see http://forums.mozillazine.org/viewtopic.php?p=2772951#p2772951 for how to get the source window of a request.
Request cancellation code from http://zenit.senecac.on.ca/wiki/index.php/Support_For_OpenID.
see http://mxr.mozilla.org/mozilla-central/source/netwerk/base/public/nsIRequest.idl for details on nsIRequest.
See http://forums.mozillazine.org/viewtopic.php?p=2404533#p2404533 and https://developer.mozilla.org/en/XUL/Method/addTab for the definition of addTab.
parseQuery comes from http://blog.strictly-software.com/2008/10/using-javascript-to-parse-querystring.html.
See https://developer.mozilla.org/en/Code_snippets/Post_data_to_window#Preprocessing_POST_data for how to process post data in a form suitable for addTab.
ReadPostFromText and ReadTextFromStream both come from firebug (though slightly modified)

Resources