如何在Firefox中使用XUL创建底部停靠面板?

时间:2014-09-21 17:23:51

标签: firefox-addon xul

对于我的插件,我想创建一个底部停靠的面板"这将显示信息。这样的例子就是Firefox自己的Web Console面板,可以在Web Developer Tools下切换。

我试图挖掘代码,但无法弄清楚这是如何实现的。有人可以给我一个关于如何用XUL创建这个的基本解释,或者指出我正确的方向吗?

2 个答案:

答案 0 :(得分:4)

Web控制台不是sidebar。在Fireox中只有一个侧边栏,它可以位于浏览器内容的左侧或右侧。即使您更改了标签,侧边栏也是UI的常量部分。它通常用于内容,历史记录,书签或其他此类信息,这些信息不会因您正在查看的标签而发生变化。

对于调查这样的内容,如果您尚未安装DOM Inspector插件,我会建议您使用getNotificationBox()插件。

Web控制台包含在<iframe>内的<tabbrowser>所在的标签下。

iframe的XUL是: <iframe xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" class="devtools-toolbox-bottom-iframe" height="436" tooltip="aHTMLTooltip" src="chrome://browser/content/devtools/framework/toolbox.xul"/>

首先<splitter>然后在<iframe>后面插入<hbox class="browserSidebarContainer">插入其中的gDevToolsBrowser.selectToolCommand(gBrowser, "webconsole");

通过调用gDevToolsBrowser

打开Web控制台 {p> chrome://browser/content/browser.jsresource:///modules/devtools/gDevTools.jsm

的内容SH_create中定义

它们实际上是在resource:///modules/devtools/framework/toolbox-hosts.js

中的chrome://函数中创建的

所有这些resource:////** * Creates an <iframe> based panel within the current tab, * or opens a window, for use as an user interface box. * If it is not a window, it is associated with the current * browser tab. * @param location * Placement of the panel [right|left|top|bottom|window] * The default location is "right". * @param size * Width if on left or right. Height if top or bottom. * Both width and height if location="window" unless * features is a string. * Default is 400. * @param id * The ID to assign to the iframe. Default is * "makyen-interface-panel" * The <splitter> will be assigned the * ID = id + "-splitter" * @param chromeUrl * This is the chrome:// URL to use for the contents * of the iframe or the window. * the default is: * "chrome://browser/content/devtools/framework/toolbox.xul" * @param features * The features string for the window. See: * https://developer.mozilla.org/en-US/docs/Web/API/Window.open * returns [splitterEl, iframeEl] * The elements for the <splitter> and <iframe> * * Copyright 2014 by Makyen. * Released under the MPL 2.0. http://mozilla.org/MPL/2.0/. **/ function createInterfacePanelIframe(location,size,id,chromeUrl,features) { //defaults size = ( (typeof size !== "number") || size<1) ? 400 : size; id = typeof id !== "string" ? "makyen-interface-panel" : id; chromeUrl = typeof chromeUrl !== "string" ? "chrome://browser/content/devtools/framework/toolbox.xul" : chromeUrl; //Create some common variables if they do not exist. // This should work from any Firefox context. // Depending on the context in which the function is being run, // this could be simplified. if (typeof window === "undefined") { //If there is no window defined, get the most recent. var window=Components.classes["@mozilla.org/appshell/window-mediator;1"] .getService(Components.interfaces.nsIWindowMediator) .getMostRecentWindow("navigator:browser"); } if (typeof document === "undefined") { //If there is no document defined, get it var document = window.content.document; } if (typeof gBrowser === "undefined") { //If there is no gBrowser defined, get it var gBrowser = window.gBrowser; } //Get the current tab & notification box (container for tab UI). let tab = gBrowser.selectedTab; let browserForTab = gBrowser.getBrowserForTab( tab ); let notificationBox = gBrowser.getNotificationBox( browserForTab ); let ownerDocument = gBrowser.ownerDocument; //Create the <iframe> use //ownerDocument for the XUL namespace. let iframeEl = ownerDocument.createElement("iframe"); iframeEl.id = id; iframeEl.setAttribute("src",chromeUrl); iframeEl.setAttribute("height", size.toString()); iframeEl.setAttribute("width", size.toString()); //Call createInterfacePanel, pass the size if it is to be a window. let splitterEl; if(location == "window" ) { splitterEl = createInterfacePanel(location, size, size ,id + "-splitter", chromeUrl, features); return [splitterEl, null]; } else { splitterEl = createInterfacePanel(location, iframeEl, iframeEl ,id + "-splitter", chromeUrl, features); } return [splitterEl, iframeEl]; } /** * Creates a panel within the current tab, or opens a window, for use as a * user interface box. If not a window, it is associated with the current * browser tab. * @param location * Placement of the panel [right|left|top|bottom|window] * The default location is "right". * @param objectEl * The element of an XUL object that will be inserted into * the DOM such that it is within the current tab. * Some examples of possible objects are <iframe>, * <browser>, <box>, <hbox>, <vbox>, etc. * If the location="window" and features is not a string * and this is a number then it is used as the width of the * window. * If features is a string, it is assumed the width is * set in that, or elsewhere (e.g. in the XUL). * @param sizeEl * The element that contains attributes of "width" and * "height". If location=="left"|"right" then the * "height" attribute is removed prior to the objectEl * being inserted into the DOM. * A spearate reference for the size element in case the * objectEl is a documentFragment containing multiple elements. * However, normal usage is for objectEl === sizeEl when * location != "window". * When location == "window" and features is not a string, * and sizeEl is a number then it is used as the height * of the window. * If features is a string, it is assumed the height is * set in that, or elsewhere (e.g. in the XUL). * @param id * The ID to assign to the <splitter>. The default is: * "makyen-interface-panel-splitter". * @param chromeUrl * This is the chrome:// URL to use for the contents * of the window. * the default is: * "chrome://browser/content/devtools/framework/toolbox.xul" * @param features * The features string for the window. See: * https://developer.mozilla.org/en-US/docs/Web/API/Window.open * returns * if location != "window": * splitterEl, The element for the <splitter>. * if location == "window": * The windowObjectReference returned by window.open(). * * Copyright 2014 by Makyen. * Released under the MPL 2.0. http://mozilla.org/MPL/2.0/. **/ function createInterfacePanel(location,objectEl,sizeEl,id,chromeUrl,features) { //Set location default: location = typeof location !== "string" ? "right" : location; if(location == "window") { if(typeof features !== "string") { let width = ""; let height = ""; if(typeof objectEl == "number") { width = "width=" + objectEl.toString() + ","; } if(typeof sizeEl == "number") { height = "height=" + sizeEl.toString() + ","; } features = width + height + "menubar=no,toolbar=no,location=no,personalbar=no" + ",status=no,chrome=yes,resizable,centerscreen"; } } id = typeof id !== "string" ? "makyen-interface-panel-splitter" : id; chromeUrl = typeof chromeUrl !== "string" ? "chrome://browser/content/devtools/framework/toolbox.xul" : chromeUrl; //Create some common variables if they do not exist. // This should work from any Firefox context. // Depending on the context in which the function is being run, // this could be simplified. if (typeof window === "undefined") { //If there is no window defined, get the most recent. var window=Components.classes["@mozilla.org/appshell/window-mediator;1"] .getService(Components.interfaces.nsIWindowMediator) .getMostRecentWindow("navigator:browser"); } if (typeof document === "undefined") { //If there is no document defined, get it var document = window.content.document; } if (typeof gBrowser === "undefined") { //If there is no gBrowser defined, get it var gBrowser = window.gBrowser; } //Get the current tab & notification box (container for tab UI). let tab = gBrowser.selectedTab; let browserForTab = gBrowser.getBrowserForTab( tab ); let notificationBox = gBrowser.getNotificationBox( browserForTab ); let ownerDocument = gBrowser.ownerDocument; //Create a Document Fragment. //If doing multiple DOM additions, we should be in the habit // of doing things in a way which causes the least number of reflows. // We know that we are going to add more than one thing, so use a // document fragment. let docFrag = ownerDocument.createDocumentFragment(); //ownerDocument must be used here in order to have the XUL namespace // or the splitter causes problems. // createElementNS() does not work here. let splitterEl = ownerDocument.createElement("splitter"); splitterEl.id = id ; //Look for the child element with class="browserSidebarContainer". //It is the element in procimity to which the <splitter> //and objectEl will be placed. let theChild = notificationBox.firstChild; while (!theChild.hasAttribute("class") || !theChild.getAttribute("class").contains("browserSidebarContainer") ) { theChild = theChild.nextSibling; if(!theChild) { //We failed to find the correct node. //This implies that the structure Firefox // uses has changed significantly and it should // be assumed that the extension is no longer compatible. return null; } } let toReturn = null; switch(location) { case "window" : return window.open(chromeUrl,"_blank",features); break; case "top" : if(sizeEl) { sizeEl.removeAttribute("width"); } docFrag.appendChild(objectEl); docFrag.appendChild(splitterEl); //Inserting the document fragment results in the same // DOM structure as if you Inserted each child of the // fragment separately. (i.e. the document fragment // is just a temporary container). //Insert the interface prior to theChild. toReturn = notificationBox.insertBefore(docFrag,theChild); break; case "bottom" : if(sizeEl) { sizeEl.removeAttribute("width"); } docFrag.appendChild(splitterEl); docFrag.appendChild(objectEl); //Insert the interface just after theChild. toReturn = notificationBox.insertBefore(docFrag,theChild.nextSibling); break; case "left" : if(sizeEl) { sizeEl.removeAttribute("height"); } docFrag.appendChild(objectEl); //Splitter is second in this orientaiton. docFrag.appendChild(splitterEl); //Insert the interface as the first child of theChild. toReturn = theChild.insertBefore(docFrag,theChild.firstChild); break; case "right" : default : //Right orientaiton, the default. if(sizeEl) { sizeEl.removeAttribute("height"); } docFrag.appendChild(splitterEl); docFrag.appendChild(objectEl); //Insert the interface as the last child of theChild. toReturn = theChild.appendChild(docFrag); break; } return splitterEl; } 网址都可以在Firefox中使用。 Firefox安装包含大量文件,包含三个文件 omni.ja 。这些文件位于&lt;安装目录&gt; /omni.ja &lt;安装目录&gt; /browser/omni.ja &lt;安装目录&gt; /webart/omni.ja omni.ja 文件只是zip格式文件,扩展名已重命名。为了方便访问这些文件,我经常将它们解压缩到目录中(在Firefox安装目录树之外)。我发现当我想弄清楚是怎么做的时候,这样可以更容易地进行搜索和操作文件。

如果您只是在查找将创建用于Web控制台的框的代码,则复杂性取决于您运行的上下文。以下几乎可以在任何地方使用:

getSidebarContainer()

注意: toolbox-hosts.js 中的代码使用方法{{1}}来查找要追加的容器。没有关于该方法的文档,因此我使用了{{3}}。

答案 1 :(得分:2)

它被称为侧栏。我在右边做了一个:https://gist.github.com/Noitidart/8728393

它是一个完整的插件,你可以安装和破解。非常基本的模板。

这是添加侧面板的部分:

    //START - EDIT BELOW HERE
    var browser = aDOMWindow.document.querySelector('#browser')
    if (browser) {
        var splitter = aDOMWindow.document.createElement('splitter');
        var propsToSet = {
            id: 'demo-sidebar-with-html_splitter',
            //class: 'sidebar-splitter' //im just copying what mozilla does for their social sidebar splitter //i left it out, but you can leave it in to see how you can style the splitter
        }
        for (var p in propsToSet) {
            splitter.setAttribute(p, propsToSet[p]);
        }

        var sidebar = aDOMWindow.document.createElement('vbox');
        var propsToSet = {
            id: 'demo-sidebar-with-html_sidebar',
            //persist: 'width' //mozilla uses persist width here, i dont know what it does and cant see it how makes a difference so i left it out
        }
        for (var p in propsToSet) {
            sidebar.setAttribute(p, propsToSet[p]);
        }

        var sidebarBrowser = aDOMWindow.document.createElement('browser');
        var propsToSet = {
            id: 'demo-sidebar-with-html_browser',
            type: 'content',
            context: 'contentAreaContextMenu',
            disableglobalhistory: 'true',
            tooltip: 'aHTMLTooltip',
            clickthrough: 'never',
            autoscrollpopup: 'autoscroller',
            flex: '1', //do not remove this
            style: 'min-width: 14em; width: 18em; max-width: 36em;', //you should change these widths to how you want
            src: 'data:text/html,%3Chtml%3E%0A%3Cbody%3E%0A%3Ciframe%20width%3D%22520%22%20height%3D%22390%22%20src%3D%22http%3A%2F%2Fweb2.0calc.com%2Fwidgets%2Fhorizontal%2F%22%20scrolling%3D%22no%22%20style%3D%22border%3A%201px%20solid%20silver%3B%20%22%3E%20%3C%2Fiframe%3E%0A%3Cbr%20%2F%3E%0A%3Ca%20href%3D%22http%3A%2F%2Fweb2.0calc.com%2F%22%3EWeb%202.0%20scientific%20calculator%3C%2Fa%3E%0A%3C%2Fbody%3E%0A%3C%2Fhtml%3E' //or just set this to some url like http://www.bing.com/
        }
        for (var p in propsToSet) {
            sidebarBrowser.setAttribute(p, propsToSet[p]);
        }

        browser.appendChild(splitter);

        sidebar.appendChild(sidebarBrowser);
        browser.appendChild(sidebar);
    }
    //END - EDIT BELOW HERE

可以复制粘贴到暂存器运行,但要从scrathpad运行,您必须先用var browser = aDomWindow.document.querySelector('#browser')替换var browser = Services.wm.getMostRecentWindow('navigator:browser').document.querySelector('#browser')