Disable Jenkins Build button based on Parameters - jenkins

I have Jenkins jobs with parameters. I would like to disable the build button and enable only user enters valid value in string parameter. How can i do that ?

There is one more extension to the above shown method - Above method checks for BUILD button on every Jenkins page as Simple Theme Plugin will be used.
In order to make the change at Job level - we can use Active Choices (https://plugins.jenkins.io/uno-choice/)
Go to configure section of the respective job
Select This project is parameterized option in General Tab
Add a new String Parameter named "Validating_Parameter"
Add another parameter of type Active Choice Reactive Reference Parameter
In Active Choice Reactive Reference Parameter - name can be empty and Choice Type should be of Formatted Hidden HTML
Add below groovy script by selecting Groovy:
return '''
<script>
function custom() {
if (!document.querySelector("td.setting-main>div>input[value='Validating_Parameter']")) {
return;
} else {
var inputElement = "";
if (document.querySelector("td.setting-main>div>input[value='Validating_Parameter']").parentNode) {
var childNodes = document.querySelector("td.setting-main>div>input[value='Validating_Parameter']").parentNode.childNodes;
if (Object.keys(childNodes).length) {
for (var key in childNodes) {
if ((Object.prototype.toString.call(childNodes[key]) == "[object HTMLInputElement]") && (childNodes[key].name == "value")) {
inputElement = childNodes[key];
}
}
}
}
}
/* init */
if (inputElement) {
var buildNumber = inputElement.value;
setTimeout(function () {
if (document.getElementsByName("parameters")[0].getElementsByTagName("button")[0]) {
document.getElementsByName("parameters")[0].getElementsByTagName("button")[0].disabled = buildNumber.match(/^[a-zA-Z0-9\\s,\\!.-]{15,}$/) ? false : true;
}
}, 100);
}
/* Register keyup event */
if (inputElement) {
inputElement.onkeyup = function () {
var buildNumber = inputElement.value;
if (document.getElementsByName("parameters")[0].getElementsByTagName("button")[0]) {
document.getElementsByName("parameters")[0].getElementsByTagName("button")[0].disabled = buildNumber.match(/^[a-zA-Z0-9\\s,\\!.-]{15,}$/) ? false : true;
}
}
}
}
document.addEventListener('DOMContentLoaded', custom, false);
</script>
'''
Updated code so it doesnt fail on yui-gen3-button not found element. you can reference answer by #Mayur
Code referenced from above code by #Renjith

You could use the validating string parameter plugin.
Or if you wanted to roll one yourself check out the documentation.
From the docs: "This is called "form validation", and Jenkins performs this on the server side." Meaning you wouldn't want to disable the build button if you use this method of validation.

Install Simple Theme plugin. This will help you to load your own JavaScript
(https://wiki.jenkins.io/display/JENKINS/Simple+Theme+Plugin )
Once its installed copy and paste following script to "/var/lib/jenkins/userContent" as custom.js
Replace "X_BUILD_NUMBER" with your string parameter which you wanted to validate.
function custom()
if (!document.querySelector("td.setting-main>div>input[value='X_BUILD_NUMBER']")) {
return;
} else{
var inputElement = "";
if(document.querySelector("td.setting-main>div>input[value='X_BUILD_NUMBER']").parentNode){
var childNodes = document.querySelector("td.setting-main>div>input[value='X_BUILD_NUMBER']").parentNode.childNodes;
if(Object.keys(childNodes).length){
for (var key in childNodes) {
if((Object.prototype.toString.call(childNodes[key]) == "[object HTMLInputElement]") && (childNodes[key].name == "value")){
inputElement = childNodes[key];
}
}
}
}
}
/* init */
if (inputElement) {
var buildNumber = inputElement.value;
setTimeout(function() {
if (document.getElementById("yui-gen1-button")) {
document.getElementById("yui-gen1-button").disabled = buildNumber ? false : true;
}
}, 100);
}
/* Register keyup event */
if (inputElement) {
inputElement.onkeyup = function() {
var buildNumber = inputElement.value;
if (document.getElementById("yui-gen1-button")) {
document.getElementById("yui-gen1-button").disabled = buildNumber ? false : true;
}
}
}
}
document.addEventListener('DOMContentLoaded', custom, false);

Thanks #Simeen Khan. I was actively searching for this. I found your post helpful but there is one thing to change based on plugins used by jenkins.
For newer jenkins and newer "Build With Parameters" plugin the documentElementID will be changed to gen4 or higher.
We can find these details using inspecting build button on build with parameter page of jenkins.
for example on my jenkins it is <button type="button" tabindex="0" id="yui-gen4-button">Build</button>.
for this Simeen's script will be like below:
return '''
<script>
function custom() {
if (!document.querySelector("td.setting-main>div>input[value='Validating_Parameter']")) {
return;
} else {
var inputElement = "";
if (document.querySelector("td.setting-main>div>input[value='Validating_Parameter']").parentNode) {
var childNodes = document.querySelector("td.setting-main>div>input[value='Validating_Parameter']").parentNode.childNodes;
if (Object.keys(childNodes).length) {
for (var key in childNodes) {
if ((Object.prototype.toString.call(childNodes[key]) == "[object HTMLInputElement]") && (childNodes[key].name == "value")) {
inputElement = childNodes[key];
}
}
}
}
}
/* init */
if (inputElement) {
var buildNumber = inputElement.value;
setTimeout(function () {
if (document.getElementById("yui-gen4-button")) {
document.getElementById("yui-gen4-button").disabled = buildNumber.match(/^[a-zA-Z0-9\\s,\\!.-]{15,}$/) ? false : true;
}
}, 100);
}
/* Register keyup event */
if (inputElement) {
inputElement.onkeyup = function () {
var buildNumber = inputElement.value;
if (document.getElementById("yui-gen4-button")) {
document.getElementById("yui-gen4-button").disabled = buildNumber.match(/^[a-zA-Z0-9\\s,\\!.-]{15,}$/) ? false : true;
}
}
}
}
document.addEventListener('DOMContentLoaded', custom, false);
</script>
'''

Related

Select2 - adding custom choice in single selection mode (tags:false)

Is it possible to setup select2 control to accept custom choice
in single selection mode (tags:false) ?
When typing your choice in the search box instead of seeing "No results found",
just see what you typing in and select it by click or pressing enter.
Well - I finally found solution myself.
I override original SelectAdapter adding new choice every time you type in
and delete previous temporary choices:
$.fn.select2.amd.require(['select2/data/select', 'select2/utils'],
function (SelectAdapter, Utils) {
function XSelectAdapter($element, options) {
XSelectAdapter.__super__.constructor.call(this, $element, options);
}
Utils.Extend(XSelectAdapter, SelectAdapter);
XSelectAdapter.prototype.query = function (params, callback) {
var data = [];
var self = this;
var $options = this.$element.children();
var have_exact_match = false;
$options.each(function () {
var $option = $(this);
if (!$option.is('option') && !$option.is('optgroup')) {
return;
}
var option = self.item($option);
if (option.xtemp === true) {
$(this).remove(); // previously typed-in choice - delete
return;
}
if (option.term === params.term) {
have_exact_match = true; // will not choice if have exact match
}
var matches = self.matches(params, option);
if (matches !== null) {
data.push(matches);
}
});
if (!have_exact_match) {
self.addOptions(this.option({selected: false, id: params.term, text: params.term, xtemp: true}));
data.push({selected: false, id: params.term, text: params.term});
}
callback({
results: data
});
};
$('#your_select2').select2({dataAdapter:XSelectAdapter});
});

breeze observableArray binding - are properties observable?

I have a viewmodel which consists of a list(foreach loop) of DoctorPrices and when clicking on an item in the list it open up a CRUD form on the side. However when i update the values on the CRUD the observableArray that is bound to the foreach is not refreshing? (although the values are updates in the DB correctly)
From my data access module i call the following query.
function getDoctorServices(doctorId) {
var query = breeze.EntityQuery
.from('DoctorPrices')
.where('DoctorID', 'eq', doctorId).orderBy('ListOrder');
return manager.executeQueryLocally(query);
}
In my viewmodel i have the following code:
this.services = ko.computed(function() {
return doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID());
});
services is bound using a foreach loop (not posting here as the code is simple and works)
When i click on a one of the DoctorPrices it gets the data as follows and places it in an observable:
this.selectedPrice = function (data, event) {
self.currentService(data);
self.showEdit(true);
};
I then bind selectPrice to a simple form that has the properties on it to be modified by the user. I then call manager.SaveChanges().
This results in the following problem: the value is being updated correctly but the GUI / Original List that is bound in the foreach is not being updated? Are the properties in breeze not observables? What is the best way to work with something like this.
I thought of a workaround and changing the code with something like this:
doctorList.viewModel.instance.currentDoctorID.subscribe(function() {
self.services([]);
self.services(doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID()));
});
But i feel that clearing the array in that way is sloppy and not the right way of doing things specially with long lists.
Can someone please point me in the right direction on how to bind observableArray properties properly so they are updated?
Additional code my VM Component:
function services() {
var self = this;
this.showForm = ko.observable(false);
this.currentService = ko.observable();
this.services = ko.observableArray(doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID()));
this.title = ko.observable();
doctorList.viewModel.instance.currentDoctorID.subscribe(function() {
self.services([]);
self.services(doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID()));
self.showDetails(false);
});
this.show = function (value) {
self.showForm(value);
};
this.showDetails = ko.observable(false);
this.addNewService = function() {
self.currentService(doctorServices.createService(doctorList.viewModel.instance.currentDoctorID()));
console.log(self.currentService().entityAspect.entityState);
self.showDetails(true);
};
this.showDelete = ko.computed(function() {
if (self.currentService() == null)
return false;
else if (self.currentService().entityAspect.entityState.isDetached()) {
self.title('Add new service');
return false;
} else {
self.title('Edit service');
return true;
}
});
this.deleteService = function() {
self.currentService().entityAspect.setDeleted();
doctorServices.saveChanges();
doctorList.viewModel.instance.currentDoctorID.notifySubscribers();
};
this.closeDetails = function () {
doctorServices.manager.rejectChanges();
doctorList.viewModel.instance.currentDoctorID.notifySubscribers();
self.showDetails(false);
};
this.selectService = function (data, event) {
self.currentService(data);
self.showDetails(true);
};
this.saveChanges = function () {
console.log(self.currentService().entityAspect.entityState);
if (self.currentService().entityAspect.entityState.isDetached()) {
doctorServices.attachEntity(self.currentService());
}
console.log(self.currentService().entityAspect.entityState);
doctorServices.saveChanges();
doctorList.viewModel.instance.currentDoctorID.notifySubscribers();
self.currentService.notifySubscribers();
self.showDetails(true);
};
}
return {
viewModel: {
instance: new services()
},
template: servicesTemplate,
};
Below is my Breeze Data Class:
define('data/doctorServices', ['jquery', 'data/dataManager', 'knockout','mod/medappBase', 'breeze', 'breeze.savequeuing'], function ($, manager, ko,base, breeze, savequeuing) {
var services = ko.observableArray([]);
return {
attachEntity:attachEntity,
getServices: getServices,
services: services,
manager:manager,
getDoctorServices: getDoctorServices,
getServiceById: getServiceById,
createService:createService,
hasChanges: hasChanges,
saveChanges: saveChanges
};
function getServices() {
var query = breeze.EntityQuery.from("DoctorPrices");
return manager.executeQuery(query).then(function (data) {
services(data.results);
}).fail(function (data) {
console.log('fetch failed...');
console.log(data);
});;
}
function getDoctorServices(doctorId) {
var query = breeze.EntityQuery
.from('DoctorPrices')
.where('DoctorID', 'eq', doctorId).orderBy('ListOrder');
var set = manager.executeQueryLocally(query);
return set;
}
function getServiceById(serviceId) {
return manager.createEntity('DoctorPrice', serviceId);
//return manager.getEntityByKey('DoctorPrice', serviceId);
}
function handleSaveValidationError(error) {
var message = "Not saved due to validation error";
try { // fish out the first error
var firstErr = error.innerError.entityErrors[0];
message += ": " + firstErr.errorMessage;
base.addNotify('error', 'Could not save.', message);
} catch (e) { /* eat it for now */ }
return message;
}
function hasChanges() {
return manager.hasChanges();
}
function attachEntity(entity) {
manager.addEntity(entity);
}
function createService(doctorId) {
return manager.createEntity('DoctorPrice', { DoctorPricingID: breeze.core.getUuid(), DoctorID:doctorId }, breeze.EntityState.Detached);
};
function saveChanges() {
return manager.saveChanges()
.then(saveSucceeded)
.fail(saveFailed);
function saveSucceeded(saveResult) {
base.addNotify('success', 'Saved.', 'Your updates have been saved.');
}
function saveFailed(error) {
var reason = error.message;
var detail = error.detail;
if (error.innerError.entityErrors) {
reason = handleSaveValidationError(error);
} else if (detail && detail.ExceptionType &&
detail.ExceptionType.indexOf('OptimisticConcurrencyException') !== -1) {
// Concurrency error
reason =
"Another user, perhaps the server, " +
"may have deleted one or all of the settings." +
" You may have to restart the app.";
} else {
reason = "Failed to save changes: " + reason +
" You may have to restart the app.";
}
console.log(error);
console.log(reason);
}
}
});
Please note this is my frist attempt at both a data class and VM. At the moment i am relying heavily on clearing the array ([]) and using notifySubscribers to make the array refresh :(
I bet you're missing an observable somewhere. I can't tell because you keep hopping from property to property whose definition is not shown.
For example, I don't know how you defined this.currentService.
I'm confused by this:
this.services = ko.computed(function() {
return doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID());
});
Why is it a ko.computed? Why not just make it an observable array.
self.service = ko.observableArray();
// ... later replace the inner array in one step ...
self.service(doctorServices.getDoctorServices(
doctorList.viewModel.instance.currentDoctorID()));
I urge you to follow the observability trail, confident that your Breeze entity properties are indeed observable.
vm.selectedPrice = ko.dependentObservable(function () {
return doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID());
}, vm);
vm is ur model on which u applied bindings , try this it will work.

Extension for intercepting a response status code

I am developing (trying) a firefox extension to intercept HTTP 500 responses status code and cancel the original request and sending it with another custom protocol.
I am trying to implement and observer and a listener but it is not working for me. I am new in this and I am sure that I am doing something wrong. Can anyone help me to figure out how to do this.
I followed the http://www.softwareishard.com/blog/firebug/nsitraceablechannel-intercept-http-traffic/ tutorial and It is not working, maybe I am not binding or using the component in the correct manner.
My code is:
Chrome.manifest
content lightweightandsecureprotocol chrome/content/
content lightweightandsecureprotocol chrome/content/ contentaccessible=yes
locale lightweightandsecureprotocol en-US crhome/locale/en-US/
skin lightweightandsecureprotocol classic/1.0 chrome/skin/
style chrome://global/content/customizeToolbar.xul chrome://lightweightandsecureprotocol/skin/browser.css
overlay chrome://browser/content/browser.xul chrome://lightweightandsecureprotocol/content/browser.xul
component {90b7bac4-78fc-4193-a2d9-1ed7a4f675eb} components/HttpResponseObserver.js
Source Code :
/chrome/content/Browser.xul
<?xml version="1.0"?>
<?xml-stylesheet type="text/css"
href="chrome://lightweightandsecureprotocol/skin/browser.css"?>
<overlay id="overlay"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/x-javascript" src="chrome://lightweightandsecureprotocol/components/HttpResponseObserver.js"/>
<script>
try{
var myComponent = Components.classes['#patricia.figueroa.millan/httpresponseobserver;1']
.getService().wrappedJSObject;
}catch(anError){
dump("ERROR:" + anError);
}
</script>
</overlay>
Source Code:
/components/HttpResponseObserver.js
const nsISupports = Components.interfaces.nsISupports;
const CLASS_ID = Components.ID("90b7bac4-78fc-4193-a2d9-1ed7a4f675eb");
const CLASS_NAME = "Http Response Observer";
const CONTRACT_ID = "#patricia.figueroa.millan/httpresponseobserver;1";
function HttpResponseObserver() {
this.wrappedJSObject = this;
}
HttpResponseObserver.prototype = {
observe: function(aSubject, aTopic, aData){
if(aTopic=="http-on-examine-response"){
let httpChannel=aSubject.QueryInterface(Components.interfaces.nsIHttpChannel);
if(httpChannel.responseStatus== 555){
alert("555 Status code in Response ");
}
var newListener=new TracingListener();
aSubject.QueryInterface(Components.interfaces.nsITraceableChannel);
newListener.originalListener=aSubject.setNewListner(newListener);
}
}
QueryInterface: function(aIID)
{
if (!aIID.equals(nsISupports))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
}
}
var HttpResponseObserverFactory = {
singleton: null,
createInstance: function (aOuter, aIID)
{
if (aOuter != null)
throw Components.results.NS_ERROR_NO_AGGREGATION;
if (this.singleton == null)
this.singleton = new HttpResponseObserver();
return this.singleton.QueryInterface(aIID);
}
};
var HttpResponseObserverModule = {
registerSelf: function(aCompMgr, aFileSpec, aLocation, aType)
{
aCompMgr = aCompMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
aCompMgr.registerFactoryLocation(CLASS_ID, CLASS_NAME, CONTRACT_ID, aFileSpec, aLocation, aType);
},
unregisterSelf: function(aCompMgr, aLocation, aType)
{
aCompMgr = aCompMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
aCompMgr.unregisterFactoryLocation(CLASS_ID, aLocation);
},
getClassObject: function(aCompMgr, aCID, aIID)
{
if (!aIID.equals(Components.interfaces.nsIFactory))
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
if (aCID.equals(CLASS_ID))
return HttpResponseObserverFactory;
throw Components.results.NS_ERROR_NO_INTERFACE;
},
canUnload: function(aCompMgr) { return true; }
};
function NSGetModule(aCompMgr, aFileSpec) { return HttpResponseObserverModule; }
//LISTENER
function TracingListener() {
this.originalListener = null;
}
TracingListener.prototype =
{
onDataAvailable: function(request, context, inputStream, offset, count) {
this.originalListener.onDataAvailable(request, context, inputStream, offset, count);
},
onStartRequest: function(request, context) {
this.originalListener.onStartRequest(request, context);
},
onStopRequest: function(request, context, statusCode) {
this.originalListener.onStopRequest(request, context, statusCode);
},
QueryInterface: function (aIID) {
if (aIID.equals(Components.interfaces.nsIStreamListener) ||
aIID.equals(Components.interfaces.nsISupports)) {
return this;
}
throw Components.results.NS_NOINTERFACE;
}
}
Thanks in advance. :D
that example is very complex, the main purpose of that traceable channel example is to get a COPY of the sourcecode that gets loaded at that uri.
const { interfaces: Ci, utils: Cu, classes: Cc, results: Cr } = Components;
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/devtools/Console.jsm');
var observers = {
'http-on-examine-response': {
observe: function (aSubject, aTopic, aData) {
console.info('http-on-examine-responset: aSubject = ' + aSubject + ' | aTopic = ' + aTopic + ' | aData = ' + aData);
var httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
if (httpChannel.responseStatus == 555) {
console.log('555 Status code in Response for request url = ' + httpChannel.URI.spec);
//httpChannel.cancel(Cr.NS_BINDING_ABORTED); //you might not need this, i think the redirectTo function handles aborting
httpChannel.redirectTo(Services.io.newURI('about:got bad response status so redirected you', null, null));
}
},
reg: function () {
Services.obs.addObserver(observers['http-on-examine-response'], 'http-on-modify-request', false);
},
unreg: function () {
Services.obs.removeObserver(observers['http-on-examine-response'], 'http-on-modify-request');
}
}
};
to register the observer on startup of addon run this:
//register all observers
for (var o in observers) {
observers[o].reg();
}
and on shutdown of addon unregister all observers like this:
//unregister all observers
for (var o in observers) {
observers[o].unreg();
}

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

Firefox SDK: How to make trigger for certain domain

I need to catch requests on sites with URLs *.net and take some actions (stop request and put HTML code from disk, but this I can do). How do I catch these requests?
I tried to use progress listeners, but something is wrong:
const STATE_START = Ci.nsIWebProgressListener.STATE_START;
var myListener = {
QueryInterface: XPCOMUtils.generateQI(["nsIWebProgressListener",
"nsISupportsWeakReference"]),
onStateChange: function(aWebProgress, aRequest, aFlag, aStatus) {
if (aFlag & STATE_START) {
// actions
}
}
use nsIHTTPChannel and observer service. copy paste it. however .net can be included in resources like javascript things, if you want to test if its specfically a window you have to check for some load flags of LOAD_INITIAL_DOCUMENT_URI, also will want to chec
Cu.import('resource://gre/modules/Services.jsm');
var httpRequestObserver = {
observe: function (subject, topic, data) {
var httpChannel, requestURL;
if (topic == "http-on-modify-request") {
httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
requestURL = httpChannel.URI.spec;
var newRequestURL, i;
if (httpChannel.loadFlags & httpChannel.LOAD_INITIAL_DOCUMENT_URI) {
//ok continue because loadFlags is a document
} else {
//its not a document, probably a resource like a js file image or css or something, but maybe could be ajax call
return;
}
if (requestURL.indexOf('.net')) {
var goodies = loadContextGoodies(httpChannel);
if (goodies) {
httpChannel.cancel(Cr.NS_BINDING_ABORTED);
goodies.contentWindow.location = self.data.url('pages/test.html');
} else {
//dont do anything as there is no contentWindow associated with the httpChannel, liekly a google ad is loading or some ajax call or something, so this is not an error
}
}
return;
}
}
};
Services.obs.addObserver(httpRequestObserver, "http-on-modify-request", false);
//this function gets the contentWindow and other good stuff from loadContext of httpChannel
function loadContextGoodies(httpChannel) {
//httpChannel must be the subject of http-on-modify-request QI'ed to nsiHTTPChannel as is done on line 8 "httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);"
//start loadContext stuff
var loadContext;
try {
var interfaceRequestor = httpChannel.notificationCallbacks.QueryInterface(Ci.nsIInterfaceRequestor);
//var DOMWindow = interfaceRequestor.getInterface(Components.interfaces.nsIDOMWindow); //not to be done anymore because: https://developer.mozilla.org/en-US/docs/Updating_extensions_for_Firefox_3.5#Getting_a_load_context_from_a_request //instead do the loadContext stuff below
try {
loadContext = interfaceRequestor.getInterface(Ci.nsILoadContext);
} catch (ex) {
try {
loadContext = subject.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
} catch (ex2) {}
}
} catch (ex0) {}
if (!loadContext) {
//no load context so dont do anything although you can run this, which is your old code
//this probably means that its loading an ajax call or like a google ad thing
return null;
} else {
var contentWindow = loadContext.associatedWindow;
if (!contentWindow) {
//this channel does not have a window, its probably loading a resource
//this probably means that its loading an ajax call or like a google ad thing
return null;
} else {
var aDOMWindow = contentWindow.top.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
var gBrowser = aDOMWindow.gBrowser;
var aTab = gBrowser._getTabForContentWindow(contentWindow.top); //this is the clickable tab xul element, the one found in the tab strip of the firefox window, aTab.linkedBrowser is same as browser var above //can stylize tab like aTab.style.backgroundColor = 'blue'; //can stylize the tab like aTab.style.fontColor = 'red';
if (aTab == null) {
return null;
}
else {
var browser = aTab.linkedBrowser; //this is the browser within the tab //this is where the example in the previous section ends
return {
aDOMWindow: aDOMWindow,
gBrowser: gBrowser,
aTab: aTab,
browser: browser,
contentWindow: contentWindow
};
}
}
//end loadContext stuff
}

Resources