Reputation: 150
I'm trying to build a Firefox Add-on using the SDK (version 1.6), but I'm running into an issue with Tabs that the extension is opening.
I would like to obtain the tab that the aContext
(node) is on. To do so, I've been 'getting' the node's window, then using the Tab Utils in the SDK, specifically getTabForContentWindow()
. This sometimes doesn't work, the Tab returned from getTabForContentWindow()
is null. Is there a better, more robust method to get a node's Tab?
Also, I noticed on the Tab Utils page, that it states it's 'unstable'. Should I avoid using Tab Utils SDK?
Below is the code from the main.js:
const {Cc, Ci, Cr, Cu, Cm, components} = require("chrome");
const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var winUtils = require('sdk/window/utils');
var tabUtils = require('sdk/tabs/utils');
let policy =
{
classDescription: "my content policy",
classID: components.ID("{2DA54ECA-FBDD-11E3-B3B1-695C1D5D46B0}"),
contractID: "@www.com/policy;1",
xpcom_categories: ["content-policy"],
init: function()
{
let registrar = Cm.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);
},
// nsIContentPolicy interface implementation
shouldLoad: function(aContentType, aContentLocation, aRequestOrigin, aContext, aMimeTypeGuess, aExtra, aRequestPrincipal) {
console.log("*****");
console.log("aContentLocation.spec [" + aContentLocation.spec + "] ");
console.log("aContentType [" + aContentType + "] ");
if (aContext instanceof components.interfaces.nsIDOMNode) {
var node = aContext.QueryInterface(components.interfaces.nsIDOMNode);
var win = getWindow(node);
if (win) {
console.log("window found" );
var selectedTab = tabUtils.getTabForContentWindow(win);
if (selectedTab) {
console.log("tab found" );
var tabId = tabUtils.getTabId(selectedTab);
console.log("Node's tabId:" + tabId);
} else {
console.log("tab undefined" );
}
} else {
console.log("win undefined" );
}
}
return Ci.nsIContentPolicy.ACCEPT;
},
shouldProcess: function(contentType, contentLocation, requestOrigin, node, mimeTypeGuess, extra) {
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();
var scheduleCheckFilterUpdates = function() {
var tabs = require("sdk/tabs");
tabs.open("http://wikipedia.org");
}
require('sdk/timers').setTimeout(scheduleCheckFilterUpdates, 1000);
function getWindow(node) {
if ("ownerDocument" in node && node.ownerDocument)
node = node.ownerDocument;
if ("defaultView" in node)
return node.defaultView;
return null;
}
Upvotes: 2
Views: 810
Reputation: 33162
You should keep in mind that:
chrome://browser/skin/tabbrowser/tab-separator.png
that the browser window itself loads (in this case to be used in the UI). Also keep in mind that not all top-level windows are actually browser windows (browser.xul
).aContentLocation.spec [http://wikipedia.org/]
) will originate from the new, not fully initialized content window that is being constructed, which is not yet associated with any tab. The tab at this point is still about:blank
and the new window (wikipedia
) will only be swapped in once it is clear that this window can be fully constructed: request wasn't blocked, DNS was resolved, redirects followed and the final response actually produced data and that data can be made into a window. E.g. if my network was down, the wikipedia
window would be abandoned before ever being assigned to the tab and instead a connection error document and window would be created.The last point is also kinda documented in the nsIContentPolicy
interface itself:
/**
* Should the resource at this location be loaded?
* ShouldLoad will be called before loading the resource at aContentLocation
* to determine whether to start the load at all.
*
* @param aContentType the type of content being tested. This will be one
* one of the TYPE_* constants.
*
* @param aContentLocation the location of the content being checked; must
* not be null
*
* @param aRequestOrigin OPTIONAL. the location of the resource that
* initiated this load request; can be null if
* inapplicable
*
* @param aContext OPTIONAL. the nsIDOMNode or nsIDOMWindow that
* initiated the request, or something that can QI
* to one of those; can be null if inapplicable.
* Note that for navigation events (new windows and
* link clicks), this is the NEW window.
*
* @param aMimeTypeGuess OPTIONAL. a guess for the requested content's
* MIME type, based on information available to
* the request initiator (e.g., an OBJECT's type
* attribute); does not reliably reflect the
* actual MIME type of the requested content
*
* @param aExtra an OPTIONAL argument, pass-through for non-Gecko
* callers to pass extra data to callees.
*
* @param aRequestPrincipal an OPTIONAL argument, defines the principal that
* caused the load. This is optional only for
* non-gecko code: all gecko code should set this
* argument. For navigation events, this is
* the principal of the page that caused this load.
*
* @return ACCEPT or REJECT_*
*
* @note shouldLoad can be called while the DOM and layout of the document
* involved is in an inconsistent state. This means that implementors of
* this method MUST NOT do any of the following:
* 1) Modify the DOM in any way (e.g. setting attributes is a no-no).
* 2) Query any DOM properties that depend on layout (e.g. offset*
* properties).
* 3) Query any DOM properties that depend on style (e.g. computed style).
* 4) Query any DOM properties that depend on the current state of the DOM
* outside the "context" node (e.g. lengths of node lists).
* 5) [JavaScript implementations only] Access properties of any sort on any
* object without using XPCNativeWrapper (either explicitly or
* implicitly). Due to various DOM0 things, this leads to item 4.
* If you do any of these things in your shouldLoad implementation, expect
* unpredictable behavior, possibly including crashes, content not showing
* up, content showing up doubled, etc. If you need to do any of the things
* above, do them off timeout or event.
*/
short shouldLoad(in nsContentPolicyType aContentType,
in nsIURI aContentLocation,
in nsIURI aRequestOrigin,
in nsISupports aContext,
in ACString aMimeTypeGuess,
in nsISupports aExtra,
[optional] in nsIPrincipal aRequestPrincipal);
The Tab Utils work as expected as far as I can tell, and so does the code from @Notidart's answer. It is the expectation that a new document is immediately assigned to a tab that is wrong.
Also, I noticed on the Tab Utils page, that it states it's 'unstable'. Should I avoid using Tab Utils SDK?
Well, that is up to your personal taste. unstable essentially means that the API can change again or even be removed again in a later version of the browser. The alternatives, like using window.gBrowser
are at least as unstable as the SDK API (also can change at any point), so IMHO there is nothing to be gained from avoiding this SDK API, provided it doesn't turned out to be buggy in some way (but if it does, you can still switch to something else).
Upvotes: 1
Reputation: 37238
This is a way you can try. From the content window you get the dom window. Then with the dom window you do the getTabForContentWindow:
//var node = focused element?
var contentWin = node.ownerDocument.defaultView;
var DOMWin = contentWin.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
if (DOMWin.gBrowser && DOMWin.gBrowser.tabContainer) {
var tab = DOMWin.gBrowser._getTabForContentWindow(contentWin);
} else {
//the node is in a firefox window with no tabs
}
Its possible for it to be a poup window with no tabs.
Upvotes: 1