Mozilla add-on and var {Cc, Ci, Cu} = require("chrome"); - firefox

i new in Mozilla addons.
I make addon who use option to off images and scripts. Used code:
let button_img = ToggleButton({ id: "btn2", label: "Show/hide images", icon: "./img1.png", onChange: imgChange});
var {Cc, Ci, Cu} = require("chrome");
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
var PrefBranch = Cc["#mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
button_img_is_cecked=0;
function imgChange(state) {
if (state.checked) {//Blocking images
button_img.icon="./img2.png";
PrefBranch.setIntPref("permissions.default.image",2);//Disable images
PrefBranch.setIntPref("permissions.default.script",2);//Disable script
button_img_is_cecked=1
}else {
button_img.icon="./img1.png";
PrefBranch.setIntPref("permissions.default.image",1);//enable images
PrefBranch.setIntPref("permissions.default.script",1);//enable script
button_img_is_cecked=0
}
}
Code and addon works correctly, but when I send addon to checking I receive message:
Usage of flagged or non-SDK interface
Warning: This SDK-based add-on uses interfaces that aren't part of the SDK or are flagged as sensitive.
var {Cc, Ci, Cu} = require("chrome");
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
I have other variants to off images and scripts without using this imported resource?
Also this code off images for all tabs in Mozilla. Can I do it jost for one tab where my addon works?

You should be able to get and set preferences using this low-level sdk module:
https://developer.mozilla.org/en-US/Add-ons/SDK/Low-Level_APIs/preferences_service
So your code would probably look like:
var gPrefs = require("sdk/preferences/service");
...
gPrefs.set("permissions.default.image",1);
gPrefs.set("permissions.default.script",1);

Related

Detect tab URL change inside a Firefox add-on

I have an extension, functional on Chrome, that monitors the active Tab for URL changes.
Specifically, I need to detect when the URL changes, but there is no new page load or navigation. Some sites do this (e.g. when you click to view another video on YouTube).
On Chrome, I accomplished this with:
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if (changeInfo && changeInfo.status == "complete") {
//do stuff here
}
});
How do I detect such changes in a Firefox add-on?
I've been told to use: Listening to events on all tabs, but I couldn't put it together. One of the problems was that gBrowser was not defined in the extension.
What am I doing wrong?
Is there a simpler way?
Use ProgressListener to be notified about location changes.
To install a listener, convert SDK tab to its raw (old) representation using viewFor.
Backward conversion is possible with modelFor and getTabForContentWindow.
const tabs = require("sdk/tabs");
const {viewFor} = require('sdk/view/core');
const {modelFor} = require('sdk/model/core');
const {getBrowserForTab, getTabForContentWindow} = require("sdk/tabs/utils");
const {Ci, Cu} = require("chrome");
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
var progressListener = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]),
onLocationChange: function(aProgress, aRequest, aURI) {
var highLevel= modelFor(getTabForContentWindow(aProgress.DOMWindow));
console.log("onLocationChange ", highLevel.url);
}
};
tabs.on('open', function(newTab) {
var lowLevel = viewFor(newTab);
var browser = getBrowserForTab(lowLevel);
browser.addProgressListener(progressListener);
});
Don't forget to remove listeners on extension unload. Tab listeners are removed automagically, but ProgressListeners won't be.
Inspired by
Converting to chrome windows
If you're using the add-on SDK, you're looking at the wrong docs. Here are the tab docs.
As stated there, you create a listener like so:
var tabs = require("sdk/tabs");
// Listen for tab openings.
tabs.on('open', function onOpen(tab) {
myOpenTabs.push(tab);
});
// Listen for tab content loads.
tabs.on('ready', function(tab) {
console.log('tab is loaded', tab.title, tab.url);
});
All the docs you look at should be a subset of developer.mozilla.org/en-US/Add-ons/SDK.
I find that the activate and pageshow events, between the two of them, cover all changes in URL that I can conjure up between switching tabs, opening pages in a new tab, closing tabs, refreshing pages, and typing in new URL's.
var updateURL = function (tab) {
var oldURL = url;
var url = tab.url;
console.log(url);
};
tabs.on("activate", updateURL);
tabs.on("pageshow", updateURL);

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 Add-on SDK and js-ctypes

I'm trying to use Firefox Add-on SDK with js-ctypes to access the methods of a local DLL, but it isn't working.
The main.js code:
var data = require("sdk/self").data;
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "mysite.com",
contentScriptFile: data.url("myjs.js")
});
The myjs.js code is just:
Components.utils.import("resource://gre/modules/ctypes.jsm");
alert("hello world");
On Firefox's console I got those messages:
The Components object is deprecated. It will soon be removed.
TypeError: Components.utils is undefined
No "hello world" alert is fired.
What's the problem? Thanks!
You cannot use js-ctypes from a content script - content scripts have no privileges. You have to do that in the extension itself, via chrome authority:
var {Cu} = require("chrome");
var {ctypes} = Cu.import("resource://gre/modules/ctypes.jsm", null);
var lib = ctypes.open(...);
This isn't a ctypes issue.
You can't alert from that context. alert is a method on window. So you can do one of two things:
Get the most recent window and run alert there.
Components.utils.import('resource://gre/modules/Services.jsm');
Services.wm.getMostRecentWindow(null).alert('hello world');
Use prompts service: example at mdn
Components.utils.import('resource://gre/modules/Services.jsm');
Services.prompts.alert(null, 'Hello World TITLE', 'hello world message');
Also if you are using addon sdk you dont have access to Components so you can't do Components.utils.import you will have to put at top of your main.js this const {Cu} = require('chrome'); then you can do Cu.import('blah')

Can't import the nsIDOMGeoGeolocation XPCOM Interface

Using the addon-sdk of firefox I'm following a tutorial about creating reusable modules, the example uses the geolocation API built into Firefox, so the code is simple:
function getCurrentPosition(callback){
var xpcomGeolocation = Cc["#mozilla.org/geolocation;1"].getService(Ci.nsIDOMGeoGeolocation);
xpcomGeolocation.getCurrentPosition(callback);
}
var widget = require("sdk/widget").Widget({
id: "whereami",
label: "Where Am I?",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: function(){
console.log("clicked!");
getCurrentPosition(function(position){
console.log("latitude: " + position.coords.latitude);
console.log("longitude: " + position.coords.longitude);
});
}
});
When running firefox with the plugin on, clicking the widget give this error:
Message: [Exception... "Component returned failure code: 0x80570018 (NS_ERROR_XPC_BAD_IID) [nsIJSCID.getService]" nsresult: "0x80570018 (NS_ERROR_XPC_BAD_IID)" location: "JS frame :: resource://gre/modules/XPIProvider.jsm -> jar:file:///tmp/tmpTFowYc.mozrunner/extensions/jid1-LIBIfbK6zvWAiQ#jetpack.xpi!/bootstrap.js -> resource://gre/modules/commonjs/toolkit/loader.js -> resource://jid1-libifbk6zvwaiq-at-jetpack/whereami/lib/main.js :: getCurrentPosition :: line 7" data: no]
According to docs, it's possible to get an error when importing nsIDGeoGeolocation so you must use Cc["#mozilla.org/geolocation;1"].getService(Ci.nsISupports); instead. Also, you must ask for permission to access geolocation, please see section Prompting for permission in
Using geolocation
reference.
By the way, as an advice, I think it will be simpler to use navigator.geolocation.getCurrentPosition(successCallback, errorCallback) since it handles prompting for permission for you, but I don't know what are you trying to do beyond this. Just to explain it a little, you could have an addon page data/index.html where you tell the user you are going to prompt him for his geolocation. Besides this, you must have a PageMod that runs a data/script.js for that addon page page, which has access to navigator.geolocation. That content script may communicate with your lib/main.js file so it has access to the user geo location. Somethings like this:
data/index.html:
<html>
<body>
<h1>Hello user!</h1>
</body>
</html>
data/script.js:
var successCallback = function(position) {
/* this way this script will talk to the pagemod in lib/main.js the user's position */
self.port.emit("gotGeolocation", position.coords.latitude, position.coords.longitude);
};
navigator.geolocation.getCurrentPosition(successCallback, errorCallback);
lib/main.js:
var data = require("sdk/self").data;
var pageMod = require("sdk/page-mod");
var tabs = require("sdk/tabs");
pageMod.PageMod({
/* attach the contentScriptFile to this html page */
include: data.url("index.html"),
contentScriptFile: data.url("script.js"),
onAttach: function(worker) {
/* listen to the script.js worker "gotGeolocation" message */
worker.port.on("gotGeolocation", function(latitude, longitude) {
console.log("latitude:", latitude);
console.log("longitude:", longitude);
});
}
});
/* this will open the index.jtml page and promt the user to access his geo position */
tabs.open(data.url("index.html"));
All this code it's only to better describe the idea, it hasn't been tested and in fact there are some indefinded objects. But I hope to give you an idea of how you can acces navigator.geolocation and pass its coords to the main.js script.
I think that you just forgot this at the top:
var { Cc, Ci } = require('chrome');
Also there a third party module for geolocation https://github.com/ZER0/geolocation

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);
}

Resources