Firefox扩展,与窗口相关的侧边栏

时间:2014-01-16 11:54:55

标签: firefox firefox-addon xul

我正在为Firefox编写扩展程序,我需要将此扩展程序的UI放在侧边栏上,我关注了一些mozilla教程,但是侧边栏与一个窗口无关。

我需要一个像UI这样的侧边栏,它会保存同一个窗口中的导航数据,并且需要它只与那个窗口相关,就像firebug一样。

到目前为止我所做的只是创建一个菜单和一个项目,我需要点击这个项目来切换我的侧边栏。

我看了一下firebug源代码,我没有在它的XUL中发现任何侧边栏的叠加,脚本对我来说很复杂,所以我不知道他们如何将他们的UI添加到窗口。

我能读到的任何想法或消息来源吗?

1 个答案:

答案 0 :(得分:1)

在谈论补充工具栏时,需要注意术语。 Mozilla在Firefox中使用的特定术语,用于" Sidebar"指的是位于UI侧面的内容框。边栏(如果打开)是UI的常量部分,其显示独立于所选选项卡。只提供一个可以选择在左侧或右侧。它通常用于不会从标签更改为标签的内容(例如书签或历史记录)。

用于Firefox devtools的UI(以及FireBug使用的展示位置)是当前标签中的子面板。它仅在调用它的选项卡中显示。它在<iframe>内实施。它也可以作为一个单独的窗口打开。

当你有一个已知的工作示例时,一种方法来弄清楚这种类型的东西是如何在DOM中实现的(整个浏览器窗口是一个DOM)是安装add-on DOM Inspector和用它来研究DOM的内容是什么样的。您可能还需要Element Inspector附加组件,它是DOM Inspector的一个非常有用的附加功能(右键单击右键可以打开DOM Inspector到单击的元素)。您可能还会发现Stacked Inspector有帮助。

了解它是如何完成的另一种方法是查看源代码。对于devtools,接口实际上是在SH_create

中的函数resource:///modules/devtools/framework/toolbox-hosts.js中创建的

当放置在底部位置时,UI将作为每个选项卡存在的<notificationbox>的子项放置。您可以使用<notificationbox>方法找到标签的gBrowser.getNotificationBox( browserForTab )。插入的元素是<splitter><iframe>。当放置在浏览器选项卡中的其他位置时,这两个元素将作为<notificationbox>的子项插入到浏览器DOM中的位置,或者作为其子<hbox>的子项class="browserSidebarContainer"插入

作为示例,以下函数将根据[location]参​​数在当前选项卡的左侧,右侧,顶部或底部创建一个面板,或者作为单独的窗口创建。默认情况下,该面板由<iframe>组成,<splitter>与浏览器内容分开/** * 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; } 。 createInterfacePanel()函数更通用,并且将接受任何元素或DOM对象作为第二个参数,该参数根据[location]在适当的位置插入DOM,并由表单内​​容分隔。此类对象应为Document Fragmentelement

{{1}}

更新

我对#34; Firefox SDK Add-on with a sidebar on both the right and left at the same time&#34;的回答显着增强了此答案中的代码。使用该答案中包含的代码而不是此处的代码可能会好得多。