An example of nsIContentPolicy for firefox addon? - firefox

I have read NsIContentPolicy and have searched whole Stackoverflow for a proper tutorial for implementing NsIContentPolicy, but all in vain. I know that Adblock uses NsIContentPolicy as their main weapon. Reverse engineering Adblock didn't help me to understand how to implement NsIContentPolicy. Is there any simple addon using NsIContentPolicy for learning, or any good tutorial on NsIContentPolicy?

I am not aware of any good tutorial but I can give you some minimal example code:
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
let policy =
{
classDescription: "Test content policy",
classID: Components.ID("{12345678-1234-1234-1234-123456789abc}"),
contractID: "#adblockplus.org/test-policy;1",
xpcom_categories: ["content-policy"],
init: function()
{
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
registrar.registerFactory(this.classID, this.classDescription, this.contractID, this);
let catMan = Cc["#mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
for each (let category in this.xpcom_categories)
catMan.addCategoryEntry(category, this.contractID, this.contractID, false, true);
onShutdown.add((function()
{
for each (let category in this.xpcom_categories)
catMan.deleteCategoryEntry(category, this.contractID, false);
// This needs to run asynchronously, see bug 753687
Services.tm.currentThread.dispatch(function()
{
registrar.unregisterFactory(this.classID, this);
}.bind(this), Ci.nsIEventTarget.DISPATCH_NORMAL);
}).bind(this));
},
// nsIContentPolicy interface implementation
shouldLoad: function(contentType, contentLocation, requestOrigin, node, mimeTypeGuess, extra)
{
dump("shouldLoad: " + contentType + " " +
(contentLocation ? contentLocation.spec : "null") + " " +
(requestOrigin ? requestOrigin.spec : "null") + " " +
node + " " +
mimeTypeGuess + "\n");
return Ci.nsIContentPolicy.ACCEPT;
},
shouldProcess: function(contentType, contentLocation, requestOrigin, node, mimeTypeGuess, extra)
{
dump("shouldProcess: " + contentType + " " +
(contentLocation ? contentLocation.spec : "null") + " " +
(requestOrigin ? requestOrigin.spec : "null") + " " +
node + " " +
mimeTypeGuess + "\n");
return Ci.nsIContentPolicy.ACCEPT;
},
// nsIFactory interface implementation
createInstance: function(outer, iid)
{
if (outer)
throw Cr.NS_ERROR_NO_AGGREGATION;
return this.QueryInterface(iid);
},
// nsISupports interface implementation
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPolicy, Ci.nsIFactory])
};
policy.init();
This comes from the minimal content policy implementation I use to look at issues with the content policies implementation - it doesn't do anything other than dumping all content policies calls to the console (window.dump documentation). Obviously, in a real implementation the fields classDescription , classID and contractID should be changed to something proper. onShutdown belongs to the private framework I am using: this extension is restartless which is why it needs to register the component "manually" and will also run this code to remove it if it is shut down during a browser session.
You can also download the complete extension: testpolicy.xpi.

Related

Only show active series in a grouped tooltip (amcharts)

I'm trying to do a tooltip similar to this:
https://codepen.io/team/amcharts/pen/dyyaxLr
But when a series is disabled via the legend (i.e. "cars"), I also want to remove the value in the tooltip.
I guess there should be a way to format the series.tooltipText with an adapter like this:
series.adapter.add("tooltipText", function (text, target) {
// generate text dynamically
// ...
return text;
});
But I can't figure out how to get only the data for the visible series and format the string accordingly.
Is something like this possible?
I found the following solution:
series.adapter.add("tooltipText", function (ev) {
var text = "[bold]{dateX}[/]\n";
x.series.each(function (item) {
if (!item.isHidden)
text +=
"[" +
item.stroke.hex +
"]●[/] " +
item.name +
": {" +
item.dataFields.valueY +
"}\n";
});
return text;
});

[InvalidStateError: "setRemoteDescription needs to called before addIceCandidate" code: 11

I create a simple video calling app by using web Rtc and websockets.
But when i run the code, the following error occured.
DOMException [InvalidStateError: "setRemoteDescription needs to called before addIceCandidate"
code: 11
I don't know how to resolve this error.
Here is my code below:
enter code here
var localVideo;
var remoteVideo;
var peerConnection;
var uuid;
var localStream;
var peerConnectionConfig = {
'iceServers': [
{'urls': 'stun:stun.services.mozilla.com'},
{'urls': 'stun:stun.l.google.com:19302'},
]
};
function pageReady() {
uuid = uuid();
console.log('Inside Page Ready');
localVideo = document.getElementById('localVideo');
remoteVideo = document.getElementById('remoteVideo');
serverConnection = new WebSocket('wss://' + window.location.hostname +
':8443');
serverConnection.onmessage = gotMessageFromServer;
var constraints = {
video: true,
audio: true,
};
if(navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia(constraints)
.then(getUserMediaSuccess).catch(errorHandler);
}else
{
alert('Your browser does not support getUserMedia API');
}
}
function getUserMediaSuccess(stream) {
localStream = stream;
localVideo.src = window.URL.createObjectURL(stream);
}
function start(isCaller) {
console.log('Inside isCaller');
peerConnection = new RTCPeerConnection(peerConnectionConfig);
peerConnection.onicecandidate = gotIceCandidate;
peerConnection.onaddstream = gotRemoteStream;
peerConnection.addStream(localStream);
if(isCaller) {
console.log('Inside Caller to create offer');
peerConnection.createOffer().
then(createdDescription).catch(errorHandler);
}
}
function gotMessageFromServer(message) {
console.log('Message from Server');
if(!peerConnection)
{
console.log('Inside !Peer Conn');
start(false);
}
var signal = JSON.parse(message.data);
// Ignore messages from ourself
if(signal.uuid == uuid) return;
if(signal.sdp) {
console.log('Inside SDP');
peerConnection.setRemoteDescription(new
RTCSessionDescription(signal.sdp)).then(function() {
// Only create answers in response to offers
if(signal.sdp.type == 'offer') {
console.log('Before Create Answer');
peerConnection.createAnswer().then(createdDescription)
.catch(errorHandler);
}
}).catch(errorHandler);
} else if(signal.ice) {
console.log('Inside Signal Ice');
peerConnection.addIceCandidate(new
RTCIceCandidate(signal.ice)).catch(errorHandler);
}
}
function gotIceCandidate(event) {
console.log('Inside Got Ice Candi');
if(event.candidate != null) {
serverConnection.send(JSON.stringify({'ice': event.candidate,
'uuid': uuid}));
}
}
function createdDescription(description) {
console.log('got description');
peerConnection.setLocalDescription(description).then(function() {
console.log('Inside Setting ');
serverConnection.send(JSON.stringify({'sdp':
peerConnection.localDescription, 'uuid': uuid}));
}).catch(errorHandler);
}
function gotRemoteStream(event) {
console.log('got remote stream');
remoteVideo.src = window.URL.createObjectURL(event.stream);
}
function errorHandler(error) {
console.log(error);
}
// Taken from http://stackoverflow.com/a/105074/515584
// Strictly speaking, it's not a real UUID, but it gets the job done here
function uuid() {
function s4() {
return Math.floor((1 + Math.random()) *
0x10000).toString(16).substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() +
s4() + s4();
}
This is my code, I don't know how to arrange the addIceCandidate and addRemoteDescription function.
You need to make sure that
peerConnection.addIceCandidate(new RTCIceCandidate(signal.ice))
is called after description is set.
You have sitution where you receive ice candidate and try to add it to peerConnection before peerConnection has completed with setting description.
I had similar situation, and I created array for storing candidates that arrived before setting description is completed, and a variable that checks if description is set. If description is set, I would add candidates to peerConnection, otherwise I would add them to array. (when you set your variable to true, you can also go through array and add all stored candidates to peerConnection.
The way WebRTC works (as much as i understand) is you have to make two peers to have a deal to how to communicate eachother in the order of give an offer to your peer get your peers answer and select an ICE candidate to communicate on then if you want to send your media streams for an video conversation
for you to have a good exampe to look on how to implemet those funcitons and in which order you can visit https://github.com/alexan1/SignalRTC he has a good understading of how to do this.
you might already have a solution to your problem at this time but im replying in case you do not.
EDIT: As I have been told, this solution is an anti-pattern and you should NOT implement it this way. For more info on how I solved it while still keeping a reasonable flow, follow this answer and comment section: https://stackoverflow.com/a/57257449/779483
TLDR: Instead of calling addIceCandidate as soon as the signaling information arrives, add the candidates to a queue. After calling setRemoteDescription, go through candidates queue and call addIceCandidate on each one.
--
From this answer I learned that we have to call setRemoteDescription(offer) before we add the Ice Candidates data.
So, expanding on #Luxior answer, I did the following:
When signaling message with candidate arrives:
Check if remote was set (via a boolean flag, ie: remoteIsReady)
If it was, call addIceCandidate
If it wasn't, add to a queue
After setRemoteDescription is called (in answer signal or answer client action):
Call a method to go through the candidates queue and call addIceCandidate on each one.
Set boolean flag (remoteIsReady) to true
Empty queue

WebExtension: React when a a tab is reloaded

I am working on a Firefox WebExtension which has a popup and uses chrome.storage.local to store state. So far, I have got it to store data for specific tabs using the tab id.
I would like to reinitialize the local storage if the current tab is reloaded. This is because the extension may have made some changes to the DOM which will be lost if the tab is reloaded; what state it has stored is therefore incorrect.
The initial JavaScript code is called from the popup.
How can I run some code when the page is reloaded?
Thanks
Without more information from you as to exactly what you desire to do, it is difficult to determine the best way to do this. In particular, it is hard to know exactly what situations to which you desire to respond.
It looks like you could use a listener to the tabs.onUpdated event. While this fires more often than you actually desire, it does fire on reloading of a page.
Below is code that calls a function completedLoadingURLInTab() when a URL has been loaded, or reloaded on a page. I've left in a bunch of console.log() calls as comments which I would normally remove. They can be uncommented to show in the console all the times the event fires. This can be useful to determine exactly the contents of the data received from the event and the sequence of events that fire during various page navigation.
Note 1: I found that the changeInfo object can be invalid under some circumstances. It was necessary to see if a property existed using hasOwnProperty() and then obtain the value from the tabs.Tab object that is also passed to the event handler.
Note 2: The tabs permission in manifest.json is required.
function completedLoadingURLInTab(tabId, newUrl,oldUrl) {
//We have completed, loading a URL.
if(newUrl === oldUrl) {
//We re-loaded the previous URL
console.log(">>>>>>> Re-load tab=" + tabId + " with URL=" + newUrl);
} else {
//This URL _may_ be just a new position in the same page. That
// is something that needs to be checked for here.
console.log(">>>>>>> New URL loaded in tab=" + tabId + ". URL=" + newUrl);
}
//Remember the newUrl so we can check against it the next time
// an event is fired.
tabsInfo[tabId].previousCompleteUrl = newUrl;
tabsInfo[tabId].loadingUrl = newUrl;
}
let tabsInfo = {};
function InfoForTab(_loadingUrl,_previousUrl,_status) {
this.loadingUrl = _loadingUrl;
this.previousCompleteUrl = (_previousUrl === undefined) ? "" : _previousUrl;
this.status = (_status === undefined) ? "" : _status;
}
function foundNewTab(tabId) {
//Create an object to hold the collected info for the tab.
tabsInfo[tabId] = new InfoForTab();
console.log("New tab. ID=" + tabId);
}
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if(!tabsInfo.hasOwnProperty(tabId)) {
//This is the first time we have encountered this tab.
foundNewTab(tabId);
}
let output="";
//We use the properties of "tab" instead of "changeInfo" because in testing it was
// clear that changeInfo was not always properly populated. The key(s) may just
// exist, but not have any value associated with them.
/*
//Testing output showing when the event is fired.
// This is used to determine, by experimentation, what events to ignore and which
// combinations and sequence of events occur during page navigation.
output += (changeInfo.hasOwnProperty("status")) ? "\nstatus=" + tab.status : "";
output += (changeInfo.hasOwnProperty("url")) ? "\nurl=" + tab.url : "";
output += (changeInfo.hasOwnProperty("pinned")) ? "\npinned=" + tab.pinned : "";
output += (changeInfo.hasOwnProperty("audible")) ? "\naudible=" + tab.audible : "";
output += (changeInfo.hasOwnProperty("mutedInfo")) ? "\nmutedInfo="+tab.mutedInfo : "";
output +=(changeInfo.hasOwnProperty("favIconUrl"))?"\nfavIconUrl="+tab.favIconUrl : "";
console.log("tabs.updated event: tabId=" + tabId + ":: changeInfo="
+ Object.keys(changeInfo) + output
);
*/
if(changeInfo.hasOwnProperty("status") && changeInfo.hasOwnProperty("url")
&& (tab.status === "loading")) {
//A URL is being loaded into the tab. This can be for the first time,
// or transitioning to a new URL, or reloading the page.
let outputFirst = "";
let outputLoading = "Loading";
if(tabsInfo[tabId].previousCompleteUrl === tab.url) {
//We are re-loading the same URL
outputLoading = "Re-loading";
}
//console.log(outputLoading + " URL=" + tab.url);
//We save the URL which is being loaded, but we really don't do anything with it.
tabsInfo[tabId].loadingUrl = tab.url;
tabsInfo[tabId].status = "loading";
return;
} //else
if(changeInfo.hasOwnProperty("status") && (tab.status === "complete")) {
if( tabsInfo[tabId].status === "loading") {
tabsInfo[tabId].status = "complete";
//console.log("In tabId="+tabId+" completed loading URL="+tab.url);
completedLoadingURLInTab(tabId, tab.url, tabsInfo[tabId].previousCompleteUrl);
return;
} //else
if( tabsInfo[tabId].status === "complete") {
if(tabsInfo[tabId].previousCompleteUrl === tab.url) {
//console.log("In tabId=" + tabId + " got completed status change after"
// + "already complete with URL="
// + tabsInfo[tabId].previousCompleteUrl);
return;
}//else
//console.log("In tabId=" + tabId + " completed directly loading new URL="
// + tab.url);
completedLoadingURLInTab(tabId, tab.url, tabsInfo[tabId].previousCompleteUrl);
return;
}
}//else
if(changeInfo.hasOwnProperty("status") && (tab.status === "loading")
&& ( tabsInfo[tabId].status === "complete")) {
//console.log("In tabId=" + tabId + " leaving page");
return;
}//else
if(changeInfo.hasOwnProperty("status") ) {
if((tab.status === "complete") && (tab.url === "about:newtab")
&& tabsInfo[tabId].loadingUrl === undefined ) {
//We have completed loading about:newtab for the first time in this tab.
tabsInfo[tabId].status = "complete";
completedLoadingURLInTab(tabId, tab.url, tabsInfo[tabId].previousCompleteUrl);
return;
} //else
//console.log("In tabId=" + tabId + " got status change to " + tab.status
// + " prior to loading a URL with current URL=" + tab.url);
return;
}//else
});
Given loading the add-on, opening a new tab and doing a bit of navigation (including a few page re-loads), the output in the console could look like:
New tab. ID=6
>>>>>>> New URL loaded in tab=6. URL=about:newtab
>>>>>>> New URL loaded in tab=6. URL=https://www.google.com/?gws_rd=ssl
>>>>>>> Re-load tab=6 with URL=https://www.google.com/?gws_rd=ssl
>>>>>>> New URL loaded in tab=6. URL=https://www.google.com/?gws_rd=ssl#q=test
>>>>>>> Re-load tab=6 with URL=https://www.google.com/?gws_rd=ssl#q=test
>>>>>>> New URL loaded in tab=6. URL=https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/Tabs/onUpdated
>>>>>>> New URL loaded in tab=6. URL=https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/Tabs/onUpdated#changeInfo
>>>>>>> Re-load tab=6 with URL=https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/Tabs/onUpdated#changeInfo
Alternately, use the webNavigation events
The webNavigation events provide you information directly about web navigation. They will probably provide you with a better solution than tabs.onUpdated.
If you use webNavigation events, you will need to experiment to see which combination of events fire for the situations your are concerned about. Most likely this will be Completed and/or ReferenceFragmentUpdated.
So you can get a feel for what when these events fire, the following code will log to the console all webNavigation events:
var webNavEvents = ['BeforeNavigate',
'Committed',
'Completed',
//'CreatedNavigationTarget', //Not supported by Firefox
'DOMContentLoaded',
'ErrorOccurred',
'HistoryStateUpdated',
'ReferenceFragmentUpdated'
//'TabReplaced' //Not supported by Firefox
];
webNavEvents.forEach(function(navType){
browser.webNavigation['on' + navType].addListener(function(type,details){
console.log('\twebNavigation->' + type
+ ': tadId=' + details.tabId
+ ':: url=' + details.url
+ ((typeof details.transitionType === 'string') ? ':: transitionType='
+ details.transitionType : '')
);
}.bind(undefined,navType));
});

Using Find.BySelector with the :visible filter in WatiN throws error

I am using WatiN 2.1 to drive Internet Explorer for automated integration testing. For one of my tests, I want to select an item in a dynamically created popup menu after clicking on a button that creates the menu. The menu itself is a jQuery plug-in that creates an unordered list with a specific class.
I am using WatiN 2.0's new Find.BySelector method to search via this specific class and that works great. However, in tests where multiple menus are created, I'm having a difficult time selecting the menu that is visible at the time. For this, I thought I would use the :visible filter to limit my results to only those menus that are visible (only one can be visible at a time). However, when I use the following code:
WebBrowser.Current.ElementOfType<List>(Find.BySelector("li.fg-menu:visible"));
I get a WatiN.Core.Exceptions.RunScriptException thrown with the message: "System.Runtime.InteropServices.COMException: Exception from HRESULT: 0x80020101" While searching for this specific HRESULT, people have recommended running Visual Studio as an Administrator, but this does not fix the problem. Without the :visible filter it works fine. When I execute that selector directly in the console window of the browser (using jQuery), it returns what I want.
To fix this, I could use WatiN's built-in ability to execute my own JavaScript to return an element, but I'm wondering if anyone else has been successful at using :visible with Find.BySelector.
For now, I've gone with this solution. It executes the selector in JavaScript using a "real" copy of jQuery already on the page. I've modified it a bit to dynamically insert the helper JavaScript based on whether it's already been inserted. It is also generic so you can specify the type of Element you want returned. Here is all the relevant extension methods for the browser object:
public static bool IsJavascriptEvaluateTrue(this Browser browser, string javascript)
{
var evalResult = browser.Eval("if (" + javascript + ") {true;} else {false;}");
return evalResult == "true";
}
public static TElementType FindViaJQuery<TElementType>(this Browser browser, string cssSelector) where TElementType : Element
{
if (!browser.IsJavascriptEvaluateTrue("typeof('WatinSearchHelper') == 'function'"))
{
var js = "WatinSearchHelper = function () { " +
" var earTagId = 1; " +
" var getElementId = function (cssSelector) { " +
" var resultId = \"_no_element_\"; " +
" var el = $(cssSelector); " +
" if (el.length > 0) { " +
" var firstEl = el[0]; " +
" if (firstEl.id == \"\") { " +
" firstEl.id = \"_watin_search_\" + earTagId++; " +
" } " +
" resultId = firstEl.id; " +
" } " +
" return resultId; " +
" }; " +
" return { getElementId: getElementId }; " +
"} (); ";
browser.Eval(js);
}
var elementId = browser.Eval(string.Format("WatinSearchHelper.getElementId(\"{0}\")", cssSelector));
if (elementId == "_no_element_")
{
throw new ArgumentException("Unable to find element on page with selector '" + cssSelector + "'", "cssSelector");
}
return browser.ElementOfType<TElementType>(Find.ById(elementId));
}

Selenium: how to wait for RichFaces ajax requests to complete

I use selenium to test a JSF/RichFaces application. The tests randomly fail due to 'Element not found' errors. This is as described in Selenium: intermittent “element not found” issues, but it relates only to jQuery ajax calls.
The challenge here is to make the selenium test execution wait for all ajax requests to complete using selenium.waitForCondition(jsExpression, timeout). What is the best jsExpression when using RichFaces ajax calls?
I investigated the generated html for the a4j:status. The code below does the job for now, it's better than wait() statements, but I'm looking for a better solution.
// depends on <a4j:status> present in the page under test
selenium.waitForCondition(
"selenium.browserbot.getCurrentWindow().document.getElementById(
"_viewRoot:status.start\").style.display == 'none'",
"3000");
I once found myself in the same situation as you. Additionally, I had a lot of elements that were actually found, but weren't visible - JSF wasn't quick enough to make them visible yet. Also, I got tired of writing selenium again and again.
So I sat down and wrote the code below. It waits for all elements to be present on the page and visible before interacting with them (or fails after a timeout). I have since moved to WebDriver, so I don't have the original code, but it was something like this:
public static long WAIT = 10000; // ten seconds
private void waitForElement(String locator) {
long targetTime = System.currentTimeMillis() + WAIT;
boolean found;
do {
found = selenium.isElementPresent(locator) && selenium.isVisible(locator);
} while (!found && (targetTime < System.currentTimeMillis()));
if (!found) {
throw new SeleniumException("Element " + locator + " not found");
}
}
public void click(String locator) {
waitForElement(locator);
selenium.click(locator);
}
public void type(String locator, String text) {
waitForElement(locator);
selenium.type(locator, text);
}
As for waitForCondition() this should be a code detecting whether an element exists or not:
String locator = "id=anything";
String script =
"var retValue = true;" +
"try {" +
" selenium.browserbot.findElement('" + locator + "');" +
"} catch(e) {" +
" retValue = false;" +
"}" +
"retValue;";
selenium.waitForCondition("!!selenium.browserbot.findElement('" + locator + "')", "10000");
selenium.click(locator);
and just the plain JavaScript:
var retValue = true;
try {
selenium.browserbot.findElement('" + locator + "');
} catch(e) {
retValue = false;
}
retValue;

Resources